1.使用FormData進(jìn)行上傳
這是比較主流的方式,也是兼容性最好的方式钓简。
前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>轉(zhuǎn)FormData傳遞數(shù)據(jù)</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);//三個參數(shù)分別對應(yīng)key value和文件名
}
formData.append('fields',JSON.stringify(fields));//文件上傳時攜帶的參數(shù)
$.ajax({
type: 'post',
url: 'http://172.31.14.33:3000/formdata',
data: formData,
processData: false,//防止數(shù)據(jù)被轉(zhuǎn)成字符串
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');
//預(yù)檢請求的有效期,單位秒,在此期間不會再次發(fā)出預(yù)檢請求
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';//設(shè)置表單域編碼
form.maxFileSize = 10 * 1024 * 1024;//設(shè)置文件的大小限制,默認(rèn)設(shè)置是200M
form.maxFieldsSize = 2 * 1024 * 1024;//限制所有存儲表單字段域的大谐叟(除去file字段,并不是限制文件的大泄睢)陨簇,如果超出,則會觸發(fā)error事件彼哼,默認(rèn)為2M
form.maxFields = 1000;//設(shè)置可以轉(zhuǎn)換多少查詢字符串对妄,默認(rèn)為1000
form.uploadDir = __dirname + '/files';//設(shè)置文件零時存放的目錄(需要建一個文件夾)
form.keepExtensions = true;//是否保存文件原有的文件擴(kuò)展名
form.hash = false;//設(shè)置上傳文件的檢驗碼,可以有兩個取值'sha1' or 'md5'.
form.multiples = true;//開啟該功能沪羔,當(dāng)調(diào)用form.parse()方法時饥伊,
form.on('progress', function (bytesReceived, bytesExpected) {
console.log((bytesReceived / bytesExpected)*100 + '%');
});
form.on('error', err => {
console.log(err);
})
// 多文件時回調(diào)函數(shù)的files參數(shù)的file屬性是一個數(shù)組,數(shù)組每一個成員是一個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進(jìn)行上傳
由于某些老舊的瀏覽器對FileReaderAPI的兼容性問題,不建議在對兼容性要求高的項目里使用篓吁。
2.1使用FileReader上傳小文件
直接將文件轉(zhuǎn)成base64字符串發(fā)給后端茫因,后端再將base64字符串中代表文件數(shù)據(jù)的字符串轉(zhuǎn)成Buffer,然后再寫成文件杖剪。
前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>轉(zhuǎn)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字符串轉(zhuǎn)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上傳大文件
當(dāng)文件較大時冻押,base64字符串會長得難以想象,這會帶來一些意料之外的問題盛嘿。這時候可以將大文件轉(zhuǎn)成ArrayBuffer進(jìn)行傳輸洛巢,后端再將ArrayBuffer轉(zhuǎn)成Buffer寫入文件。
2.2.1不攜額外參數(shù)時
前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>轉(zhuǎn)ArrayBuffer上傳文件次兆,不帶其他參數(shù)</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',//也可以不設(shè)置
},
processData:false,//防止數(shù)據(jù)被轉(zhuǎn)成字符串
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');
//預(yù)檢請求的有效期,單位秒,在此期間不會再次發(fā)出預(yù)檢請求
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);//轉(zhuǎn)成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需要攜帶額外參數(shù)時
可以在前端將ArrayBuffer轉(zhuǎn)成Array稿茉,后端接收后再將Array轉(zhuǎn)成Buffer即可。
前端代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>轉(zhuǎn)ArrayBuffer上傳文件芥炭,帶其他參數(shù)</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轉(zhuǎn)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');
//設(shè)置允許的請求方法
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
//預(yù)檢請求的有效期,單位秒,在此期間不會再次發(fā)出預(yù)檢請求
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轉(zhuǎn)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)