直奔主题,就不墨迹了,文件数据是post数据,html表单里面post支持三种enctype,
const http = require('http');
const fs = require('fs');
let server = http.createServer((req,res) => {
let str = '';
req.on('data',(data) => {
str+=data;
});
req.on('end',() => {
console.log(str);
res.end();
})
})
server.listen(8080, () => {
console.log(`服务器已启动,监听在8080端口`);
});
复制代码
html
<form action="http://localhost:8080" enctype="multipart/form-data" method="post">
<input type="text" name="user" id="">
<input type="password" name="pass" id="">
<input type="file" name="f1" id="">
<input type="submit" value="提交">
</form>
复制代码
得到的结果是这样的
分隔符
Content-Disposition: form-data; name="user"
chen
分隔符
Content-Disposition: form-data; name="pass"
123321`1
分隔符
Content-Disposition: form-data; name="f1"; filename="test.html"
Content-Type: text/html
<!DOCTYPE html>
<html lang="en">
<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">
复制代码
然后把换行换成\r\n,数据就变成了这样
<分隔符>\r\n
Content-Disposition: form-data; name="user"\r\n
\r\n
chen\r\n
<分隔符>\r\n
Content-Disposition: form-data; name="pass"\r\n
\r\n
123321`1\r\n
<分隔符>\r\n
Content-Disposition: form-data; name="f1"; filename="test.html"\r\n
Content-Type: text/html\r\n
\r\n
<文件内容>\r\n
<分隔符>--
复制代码
然后把Content-Disposition: form-data;用数据类型描述换一下,为了方便分析数据,把它对应的值换成数据值。
<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述\r\n\r\n数据值\r\n
<分隔符>\r\n数据描述1\r\n数据描述2\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\r\n数据描述2\r\n\r\n<文件内容>\r\n,
--\r\n
]
这里的空,可能有些疑惑,不过你反应5秒钟应该就不疑惑了。空和最后一个数据没有价值,
于是我们把他们去掉,于是数据就变成了这样
[
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述\r\n\r\n数据值\r\n,
\r\n数据描述1\r\n数据描述2\r\n\r\n<文件内容>\r\n,
]
复制代码
继续,然后观察一下这个数据,每行头和尾都有一个\r\n,把这个也去掉,数据就变成了这样
数据描述\r\n\r\n数据值,
数据描述\r\n\r\n数据值,
数据描述1\r\n数据描述2\r\n\r\n<文件内容>,
]
复制代码
然后再用\r\n\r\n对数据在切一次,就变成了这样
普通数据:[数据描述, 数据值]
或
文件数据:[数据描述1\r\n数据描述2, <文件内容>]
复制代码
到这就可以动手写代码了,
const http=require('http');
const fs=require('fs');
const uuid=require('uuid/v4');
Buffer.prototype.split=Buffer.prototype.split||function (b){
let arr=[];
let cur=0;
let n=0;
while((n=this.indexOf(b, cur))!=-1){
arr.push(this.slice(cur, n));
cur=n+b.length;
}
arr.push(this.slice(cur));
return arr;
};
let server=http.createServer((req, res)=>{
let arr=[];
req.on('data', data=>{
arr.push(data);
});
req.on('end', ()=>{
let data=Buffer.concat(arr);
//data
//解析二进制文件上传数据
let post={};
let files={};
if(req.headers['content-type']){
let str=req.headers['content-type'].split('; ')[1];
if(str){
let boundary='--'+str.split('=')[1];
//1.用"分隔符切分整个数据"
let arr=data.split(boundary);
//2.丢弃头尾两个数据
arr.shift();
arr.pop();
//3.丢弃掉每个数据头尾的"\r\n"
arr=arr.map(buffer=>buffer.slice(2,buffer.length-2));
//4.每个数据在第一个"\r\n\r\n"处切成两半
arr.forEach(buffer=>{
let n=buffer.indexOf('\r\n\r\n');
let disposition=buffer.slice(0, n);
let content=buffer.slice(n+4);
disposition=disposition.toString();
if(disposition.indexOf('\r\n')==-1){
//普通数据
//Content-Disposition: form-data; name="user"
content=content.toString();
let name=disposition.split('; ')[1].split('=')[1];
name=name.substring(1, name.length-1);
post[name]=content;
}else{
//文件数据
/*Content-Disposition: form-data; name="f1"; filename="a.txt"\r\n
Content-Type: text/plain*/
let [line1, line2]=disposition.split('\r\n');
let [,name,filename]=line1.split('; ');
let type=line2.split(': ')[1];
name=name.split('=')[1];
name=name.substring(1,name.length-1);
filename=filename.split('=')[1];
filename=filename.substring(1,filename.length-1);
let path=`upload/${uuid().replace(/\-/g, '')}`;
fs.writeFile(path, content, err=>{
if(err){
console.log('文件写入失败', err);
}else{
files[name]={filename, path, type};
console.log(files);
}
});
}
});
//5.完成
console.log(post);
}
}
res.end();
});
});
server.listen(8080,() => {
console.log('服务器启动在8080端口);
})
复制代码
到这就可以测试了。
灵感来源blue课堂。