1.使用FormData进行上传
这是比较主流的方式,也是兼容性最好的方式。
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转FormData传递数据</title>
</head>
<body>
<input type="file" id="file" multiple>
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
let $file = document.getElementById('file');
$file.addEventListener('change', function (e) {
let files = e.target.files;
let formData=new FormData();
let fields={
id:'007',
username:'张三',
}
for (const file of files) {
formData.append('file',file,file.name);//三个参数分别对应key value和文件名
}
formData.append('fields',JSON.stringify(fields));//文件上传时携带的参数
$.ajax({
type: 'post',
url: 'http://172.31.14.33:3000/formdata',
data: formData,
processData: false,//防止数据被转成字符串
contentType:false,
success: function (res) {
console.log(res);
},
error: function (err) {
console.log(err);
}
})
})
</script>
</html>
后端代码
const http = require('http')
const port = 3000
const fs = require('fs')
const formidable = require('formidable')
http.createServer((req, res) => {
//跨域处理
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
//预检请求的有效期,单位秒,在此期间不会再次发出预检请求
res.setHeader('Access-Control-Max-Age', 36000)
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
}
let path = req.url.split('?')[0];
if (path === '/formdata') {
let form = formidable.IncomingForm();
form.encoding = 'utf-8';//设置表单域编码
form.maxFileSize = 10 * 1024 * 1024;//设置文件的大小限制,默认设置是200M
form.maxFieldsSize = 2 * 1024 * 1024;//限制所有存储表单字段域的大小(除去file字段,并不是限制文件的大小),如果超出,则会触发error事件,默认为2M
form.maxFields = 1000;//设置可以转换多少查询字符串,默认为1000
form.uploadDir = __dirname + '/files';//设置文件零时存放的目录(需要建一个文件夹)
form.keepExtensions = true;//是否保存文件原有的文件扩展名
form.hash = false;//设置上传文件的检验码,可以有两个取值'sha1' or 'md5'.
form.multiples = true;//开启该功能,当调用form.parse()方法时,
form.on('progress', function (bytesReceived, bytesExpected) {
console.log((bytesReceived / bytesExpected)*100 + '%');
});
form.on('error', err => {
console.log(err);
})
// 多文件时回调函数的files参数的file属性是一个数组,数组每一个成员是一个File对象,此功能需要 html5中multiple特性支持。
form.parse(req, function (err, fields, files) {
let info = JSON.parse(fields.fields);
console.log(info);//{ id: '007', username: '张三' }
console.log(files);
if (files.file.length) {
//多文件
let count = 0;
for (const file of files.file) {
//更改文件名
fs.rename(file.path, './files/' + file.name + '', err => {
if (err) {
console.log(err);
res.writeHead(500);
res.end(JSON.stringify({files, fields}));
}
count++;
if (count === files.file.length) {
res.writeHead(200);
res.end(JSON.stringify({files, fields}));
}
})
}
} else {
//单文件
let file = files.file;
//更改文件名
fs.rename(file.path, './files/' + file.name + '', err => {
if (err) {
console.log(err);
res.writeHead(500);
res.end(JSON.stringify({files, fields}));
}
res.writeHead(200);
res.end(JSON.stringify({files, fields}));
})
}
})
}
}).listen(port)
2.使用FileReader进行上传
由于某些老旧的浏览器对FileReaderAPI的兼容性问题,不建议在对兼容性要求高的项目里使用。
2.1使用FileReader上传小文件
直接将文件转成base64字符串发给后端,后端再将base64字符串中代表文件数据的字符串转成Buffer,然后再写成文件。
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转base64上传文件</title>
</head>
<body>
<input type="file" id="file">
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
let $file=document.getElementById('file');
$file.addEventListener('change',function (e) {
let file=e.target.files[0];
let filename=file.name;
console.log(file);
let fileReader=new FileReader();
fileReader.onload=function(e){
let base64=e.target.result;//base64字符串
$.ajax({
type:'post',
url:'http://172.31.14.33:3000/base64File',
data:JSON.stringify({
filename:filename,
base64:base64,
}),
success:function (res) {
console.log(res);
},
error:function (err) {
console.log(err);
}
})
}
fileReader.readAsDataURL(file);
})
</script>
</html>
后端代码
const http = require('http')
const port = 3000
const fs = require('fs')
http.createServer((req, res) => {
//跨域处理
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
res.setHeader('Access-Control-Max-Age', 36000)
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
}
let path=req.url.split('?')[0];
if(path==='/base64File'&&req.method==='POST'){
let str='';
req.on('data',function (chunk) {
str+=chunk;
})
req.on('end',function () {
let data=JSON.parse(str);
let buffer=Buffer.from(data.base64.split(',')[1],'base64');//base64字符串转Buffer
fs.writeFile('./'+data.filename,buffer,function (err) {
if(err){
console.log(err);
res.writeHead(500);
res.end('error');
}else {
res.writeHead(200);
res.end('success');
}
})
})
}
}).listen(port)
2.2使用FileReader上传大文件
当文件较大时,base64字符串会长得难以想象,这会带来一些意料之外的问题。这时候可以将大文件转成ArrayBuffer进行传输,后端再将ArrayBuffer转成Buffer写入文件。
2.2.1不携额外参数时
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转ArrayBuffer上传文件,不带其他参数</title>
</head>
<body>
<input type="file" id="file">
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
let $file = document.getElementById('file');
$file.addEventListener('change', function (e) {
let file = e.target.files[0];
let fileReader = new FileReader();
fileReader.onload = function (e) {
let arraybuffer = e.target.result;//ArrayBuffer
$.ajax({
type: 'post',
url: 'http://172.31.14.33:3000/arraybuffer_no_options',
data: arraybuffer,
headers:{
'Content-Type':'application/octet-stream',//也可以不设置
},
processData:false,//防止数据被转成字符串
success: function (res) {
console.log(res);
},
error: function (err) {
console.log(err);
}
})
}
fileReader.readAsArrayBuffer(file);
})
</script>
</html>
后端代码
const http = require('http')
const port = 3000
const fs = require('fs')
http.createServer((req, res) => {
//跨域处理
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
//预检请求的有效期,单位秒,在此期间不会再次发出预检请求
res.setHeader('Access-Control-Max-Age', 36000)
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
}
let path = req.url.split('?')[0];
if (path === '/arraybuffer_no_options' && req.method === 'POST') {
let arr = [];
req.on('data', function (chunk) {
arr.push(chunk);
})
req.on('end', function () {
let buffer = Buffer.concat(arr);//转成Buffer
console.log(buffer);
fs.writeFile('./test.png', buffer, function (err) {
if (err) {
console.log(err);
res.writeHead(500);
res.end('error');
} else {
res.writeHead(200);
res.end('success');
}
})
})
}
}).listen(port)
2.2.2需要携带额外参数时
可以在前端将ArrayBuffer转成Array,后端接收后再将Array转成Buffer即可。
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>转ArrayBuffer上传文件,带其他参数</title>
</head>
<body>
<input type="file" id="file">
</body>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script>
let $file = document.getElementById('file');
$file.addEventListener('change', function (e) {
let file = e.target.files[0];
let fileReader = new FileReader();
fileReader.onload = function (e) {
let arraybuffer = e.target.result;//ArrayBuffer
//ArrayBuffer转Array
let array = Array.prototype.slice.call(new Uint8Array(arraybuffer));
$.ajax({
type: 'post',
url: 'http://172.31.14.33:3000/arraybuffer_has_options',
data: JSON.stringify({
filename:file.name,
u8arr:array,
}),
success: function (res) {
console.log(res);
},
error: function (err) {
console.log(err);
}
})
}
fileReader.readAsArrayBuffer(file);
})
</script>
</html>
后端代码
const http = require('http')
const port = 3000
const fs = require('fs')
http.createServer((req, res) => {
//跨域处理
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
//设置允许的请求方法
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
//预检请求的有效期,单位秒,在此期间不会再次发出预检请求
res.setHeader('Access-Control-Max-Age', 36000);
if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
}
let path=req.url.split('?')[0];
if(path==='/arraybuffer_has_options'&&req.method==='POST'){
let msg='';
req.on('data',function (chunk) {
msg+=chunk;
})
req.on('end',function () {
let data=JSON.parse(msg);
let buffer = new Buffer.from(data.u8arr);//Array转Buffer
fs.writeFile('./'+data.filename,buffer,function (err) {
if(err){
console.log(err);
res.writeHead(500);
res.end('error');
}else {
res.writeHead(200);
res.end('success');
}
})
})
}
}).listen(port)