NodeJs原生文件上传理解

使用nodejs实现文件上传比较麻烦,本文只是方便理解原理简单实现
1.准备html页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
	<form action="http://localhost:8080" enctype="multipart/form-data">
		姓名:<input type="text" name="username">
		<br />
		密码:<input type="password" name="password">
		<br />
		文件:<input type="file" name='file'>
		<br />
		<input type="submit" value="提交" name="">
	</form>
    
</body>
</html>

2.编写nodejs代码接收前台数据

const http = require('http');
http.createServer((req,res)=>{
	let arr = [];
	req.on('data',buffer=>{
		arr.push(buffer);
	})
	req.on('end',()=>{
		console.log(arr.toString());
	})
}).listen(8080);

这里我们为了方便查看提交的数据,我们将buffer数据转换为普通字符串
当提交时结果如下‘
在这里插入图片描述

在这里插入图片描述

可以看到数据被分隔符(-----WebKit…)分隔成了好几段
第一段:用户名
第二段:密码
第三段:文件内容
文件内容多了一行文件类型说明

数据获取成功,但我们怎么将这些数据格式化 如(username:dfdf,password:dedse....)的形式在不借助第三方模块的情况下。
通过观察我们发现,他的数据结构大概是这样

<分隔符>\r\n字段信息\r\n\r\n内容\r\n<分隔符>\r\n字段信息\r\n\r\n内容\r\n<分隔符>\r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n<分隔符>--
\r\n表示换行。

要将这些数据格式化,首先我们先从<分隔符>切割,当分隔符被切割后,上面数据应该变为
1.

[
    null,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n,
    --
]

2.此时在将第0个和最后一个去掉得到

[
 \r\n字段信息\r\n\r\n内容\r\n,
 \r\n字段信息\r\n\r\n内容\r\n,
 \r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n,
]

3.在将数组中的每个元素的第一个换行和最后一个换行去掉得到如下

[
 字段信息\r\n\r\n内容,
 字段信息\r\n\r\n内容,
 字段信息\r\n文件类型\r\n\r\n内容,
]

4.对于普通数据我们在通过字符串切割,切掉\r\n\r\n即可

字段信息,内容
字段信息,内容
...

思路大致明了,但前台提交的是buffer数据,我们不能直接将其转换为字符串在通过字符串的方法进行切割。

不过好在Buffer为我们提供了一些方法,这些方法与字符串的方法一样。
buffer.indexOf(str):查找str在buffer数据中的位置
buffer.slice(start,end) 类似于js中的substring

注意:buffer中没有切分数据的方法

通过以上两个方法我们就能实现buffer数据的切分
如下示例

let buffer = new Buffer('abc\r\nbsdk\r\nrgg');//定义一个buffer数据,使用分隔符隔开
function bufferSplit(buffer,deli){
	let arr = [];//存储最终数据
	let n = 0;//分隔符开始的位置
	//如果分隔符存在
	while((n=buffer.indexOf(deli))!=-1){
		arr.push(buffer.slice(0,n));//abc
		buffer=buffer.slice(n+deli.length);
		//第一次buffer=bsdk\r\n\rgg
		//第二次buffer=\r\nrgg
		..
	}
	arr.push(buffer);
	return arr;
}

console.log(bufferSpli(buffer,'\r\n').toString());
//返回 abc,bsdk,rgg

我们通过上面的函数加简单的分隔了buffer数据,现在我们将他应用到文件上传项目上

将这个函数定义成一个模块

exports.bufferSplit=function(buffer,deli){
	let arr = [];
	let n = 0;
	while((n=buffer.indexOf(deli))!=-1){

		arr.push(buffer.slice(0,n));
		buffer=buffer.slice(n+deli.length);
	}
	arr.push(buffer);
	return arr;
}

使用这个模块切割分隔符
上面我们所说的分隔符其实在 req.headers

const http = require('http');
const util = require('uti');
http.createServer((req,res)=>{
	console.log(req.headers);
	let arr = [];
	req.on('data',buffer=>{
		arr.push(buffer);
	})
	req.on('end',()=>{
		let buffer = Buffer.concat(arr);
		
	})
}).listen(8080);

在这里插入图片描述
需要注意的是headers里面的分隔符比我们最后得到的分隔符少两个‘-’,我们在获取分隔符的时候要注意加两个-

//最后得到:
------WebKitFormBoundary0qjsU7Tl6U1BApvt

//headers:
----WebKitFormBoundary0qjsU7Tl6U1BApvt

获取分隔符片段

let boundary='--'+req.headers['content-type'].split('; ')[1].split('=')[1];
console.log(boundary);
//------WebKitFormBoundaryn3lpgLIrRcqpTTWi

切割分隔符

const http = require('http');
const util = require('uti');
http.createServer((req,res)=>{
	// console.log(req.headers);
	let boundary='--'+req.headers['content-type'].split('; ')[1].split('=')[1];
	// console.log(boundary);

	let arr = [];
	req.on('data',buffer=>{
		arr.push(buffer);
	})
	req.on('end',()=>{

		let buffer = Buffer.concat(arr);
		//切割分隔符
		let result = util.bufferSplit(buffer,boundary);
		console.log(result.toString());
	})
}).listen(8080);

数据和我们料想的一样

[
    null,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n字段信息\r\n\r\n内容\r\n,
    \r\n\字段信息\r\n文件类型\r\n\r\n内容\r\n,
    --
]

在这里插入图片描述
删除第一行和最后一个元素‘

req.on('end',()=>{
		let buffer = Buffer.concat(arr);
		//切割分隔符
		let result = util.bufferSplit(buffer,boundary);
		//删除第一个和最后一个元素
	    result.pop();
	    result.shift();
		console.log(result.toString());


	})

对数组中的每一个元素处理

req.on('end',()=>{
		let buffer = Buffer.concat(arr);
		//切割分隔符
		let result = util.bufferSplit(buffer,boundary);
	    result.pop();
	    result.shift();
		// console.log(result.toString());
		//对每一个元素进行处理
		result.forEach(buffer=>{
			buffer = buffer.slice(2,buffer.length-2);//切掉buffer前面和后面的换行
			let n = buffer.indexOf('\r\n\r\n');//获取分分隔符的位置
			let info = buffer.slice(0, n).toString();//获取字段信息
			let data = buffer.slice(n+4);//获取字段内容

			console.log(info);
			console.log(data.toString());
		})


	})

打印结构如下
在这里插入图片描述
判断文件数据和普通数据获取字段名/文件名

req.on('end',()=>{
		let buffer = Buffer.concat(arr);
		//1.切割分隔符
		let result = util.bufferSplit(buffer,boundary);
	    result.pop();
	    result.shift();
		// console.log(result.toString());
		//2.对每一个元素进行处理
		result.forEach(buffer=>{
			//3.切断buffer前面和后面的换行
			buffer = buffer.slice(2,buffer.length-2);
            //4.去掉每个元素的\r\n\r\n
			let n = buffer.indexOf('\r\n\r\n');//获取分分隔符的位置
			let info = buffer.slice(0, n).toString();//获取字段信息

			let data = buffer.slice(n+4);//获取字段内容
			// console.log(info);
			// console.log(data.toString());
			//5.判断文件和普通数据
			if(info.indexOf('\r\n')!=-1){
				//5.1文件信息 获取字段名和文件名
				let res2 = info.split('\r\n')[0].split('; ');
				let name = res2[1].split('=')[1];//获取字段名
				let filename = res2[2].split('=')[1];//获取文件名
				name = name.substring(1, name.length-1);//去掉引号
				filename = filename.substring(1, filename.length-1);
				console.log(name);
			    console.log(filename);
			}else{
				//5.2普通信息 获取字段名
				let name = info.split('; ')[1].split('=')[1];
				name = name.substring(1, name.length-1);
				console.log(name+':'+data);
				//username:admin
				//password:23456754
			}
		})

	})

通过fs模块实现文件上传

req.on('end',()=>{

		let buffer = Buffer.concat(arr);
		//1.切割分隔符
		let result = util.bufferSplit(buffer,boundary);
	    result.pop();
	    result.shift();
		// console.log(result.toString());
		//2.对每一个元素进行处理
		result.forEach(buffer=>{
			//3.切断buffer前面和后面的换行
			buffer = buffer.slice(2,buffer.length-2);
            //4.去掉每个元素的\r\n\r\n
			let n = buffer.indexOf('\r\n\r\n');//获取分分隔符的位置
			let info = buffer.slice(0, n).toString();//获取字段信息

			let data = buffer.slice(n+4);//获取字段内容
			// console.log(info);
			// console.log(data.toString());
			//5.判断文件和普通数据
			if(info.indexOf('\r\n')!=-1){
				//5.1文件信息 获取字段名和文件名
				let res2 = info.split('\r\n')[0].split('; ');
				let name = res2[1].split('=')[1];//获取字段名
				let filename = res2[2].split('=')[1];//获取文件名
				name = name.substring(1, name.length-1);//去掉引号
				filename = filename.substring(1, filename.length-1);
				// console.log(name);
				// console.log(filename);
				//文件上传
				fs.writeFile(`upload/${filename}`,data,err=>{
					if(err){
						console.log(err);
					}else{
						console.log('上传成功');
					}
				});

			}else{
				//5.2普通信息 获取字段名
				let name = info.split('; ')[1].split('=')[1];
				name = name.substring(1, name.length-1);
				console.log(name+':'+data);
			}
		})

	})

在这里插入图片描述
成功上传文件
在这里插入图片描述
到这里使用nodejs原生简单实现文件上传已经完成,但实在是太麻烦了,且还有很多没完善的地方,本文只为理解原理。

在我们真正做文件上传时可以借助第三方模块来完成
如multiparty模块
官方网址:https://www.npmjs.com/package/multiparty

通过multiparty实现文件上传

const http = require('http');
const multiparty = require('multiparty');
http.createServer((req,res)=>{
	let form = new multiparty.Form({
		uploadDir:'./upload'//指定上传的文件路径
	});
	form.parse(req);
    //接收普通数据
	form.on('field',(name,value)=>{
		// name:字段名
		// value:值
		console.log('数据:',name,value);

	})
	//接收文件数据
	form.on('file',(name,file)=>{
		console.log('文件:',name,file);
	})
	//表单解析完成
	form.on('close',()=>{
		console.log('完成');
	})
}).listen(8080);

在这里插入图片描述

原创文章 134 获赞 9 访问量 8780

猜你喜欢

转载自blog.csdn.net/weixin_45143481/article/details/105142575
今日推荐