有時(shí)候就是有這樣的需求,Nodejs做webserver,從瀏覽器端上傳文件到后端服務(wù)器,Node層只是做一個(gè)數(shù)據(jù)中轉(zhuǎn),如果在這個(gè)過程中,Node webserver需要對(duì)數(shù)據(jù)進(jìn)行適當(dāng)加工,然后再Post到后端,那么就得在Node層模擬文件上傳了。
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比阿拉爾網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式阿拉爾網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋阿拉爾地區(qū)。費(fèi)用合理售后完善,10余年實(shí)體公司更值得信賴。
首先,通過瀏覽器上傳文件,PostData格式是長著個(gè)樣子的:
屏幕快照 2014-11-22 下午9.18.45.png
如圖,每一組數(shù)據(jù)其實(shí)就是用“-----WebkitFormBoundary.....”分隔開的,最后再用這個(gè)分隔符結(jié)束,而且,這個(gè)分隔符完全是可自定義的。
每一段提交數(shù)據(jù),則通過Content-Disposition來描述,未指定Content-Type,則默認(rèn)text/plain,如果是上傳的二進(jìn)制文件,指定其mime-type即可。
簡(jiǎn)單封裝一個(gè)方法,實(shí)現(xiàn)Node層的文件上傳:
/** * 上傳文件 * @param files 經(jīng)過formidable處理過的文件 * @param req httpRequest對(duì)象 * @param postData 額外提交的數(shù)據(jù) */ function uploadFile(files, req, postData) { var boundaryKey = Math.random().toString(16); var endData = '\r\n----' + boundaryKey + '--'; var filesLength = 0, content; // 初始數(shù)據(jù),把post過來的數(shù)據(jù)都攜帶上去 content = (function (obj) { var rslt = []; Object.keys(obj).forEach(function (key) { arr = ['\r\n----' + boundaryKey + '\r\n']; arr.push('Content-Disposition: form-data; name="' + key + '"\r\n\r\n'); arr.push(obj[key]); rslt.push(arr.join('')); }); return rslt.join(''); })(postData); // 組裝數(shù)據(jù) Object.keys(files).forEach(function (key) { if (!files.hasOwnProperty(key)) { delete files.key; return; } content += '\r\n----' + boundaryKey + '\r\n' + 'Content-Type: application/octet-stream\r\n' + 'Content-Disposition: form-data; name="' + key + '"; ' + 'filename="' + files[key].name + '"; \r\n' + 'Content-Transfer-Encoding: binary\r\n\r\n'; files[key].contentBinary = new Buffer(content, 'utf-8'); filesLength += files[key].contentBinary.length + fs.statSync(files[key].path).size; }); req.setHeader('Content-Type', 'multipart/form-data; boundary=--' + boundaryKey); req.setHeader('Content-Length', filesLength + Buffer.byteLength(endData)); // 執(zhí)行上傳 var allFiles = Object.keys(files); var fileNum = allFiles.length; var uploadedCount = 0; allFiles.forEach(function (key) { req.write(files[key].contentBinary); var fileStream = fs.createReadStream(files[key].path, {bufferSize: 4 * 1024}); fileStream.on('end', function () { // 上傳成功一個(gè)文件之后,把臨時(shí)文件刪了 fs.unlink(files[key].path); uploadedCount++; if (uploadedCount == fileNum) { // 如果已經(jīng)是最后一個(gè)文件,那就正常結(jié)束 req.end(endData); } }); fileStream.pipe(req, {end: false}); }); }
思路就這樣,代碼也不復(fù)雜,可能額外需要注意的是,在http.request的response處理中,response.headers可能是gzip的,這個(gè)時(shí)候buffer不能直接toString,需要通過zlib解碼再轉(zhuǎn)換為string,大概思路:
var result = []; response.on('data', function (chunk) { result.push(chunk); }); // 處理response var _dealResponse = function (data) { var buffer = data; try { data = data.toString('utf8'); data = data ? (JSON.parse(data) || data) : false; } catch (err) { // 接口返回?cái)?shù)據(jù)格式異常,解析失敗 console.log(err); } self.res.writeHead(response.statusCode, 'OK', { 'content-type': 'text/plain; charset=utf-8', 'content-length': buffer.length }); self.res.write(buffer); self.res.end(); }; response.on('end', function () { result = Buffer.concat(result); // gzip 的數(shù)據(jù),需要zlib解碼 if (response.headers['content-encoding'] == 'gzip') { zlib.gunzip(result, function (err, dezipped) { var data = err ? new Buffer('{}') : dezipped; _dealResponse(data); }); } else { _dealResponse(result); } });
Mark一下,也許你路過正好需要~~~
以上這篇Node層模擬實(shí)現(xiàn)multipart表單的文件上傳示例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持創(chuàng)新互聯(lián)。