Node层模拟实现multipart表单的文件上传示例


Posted in Javascript onJanuary 02, 2018

有时候就是有这样的需求,Nodejs做webserver,从浏览器端上传文件到后端服务器,Node层只是做一个数据中转,如果在这个过程中,Node webserver需要对数据进行适当加工,然后再Post到后端,那么就得在Node层模拟文件上传了。

首先,通过浏览器上传文件,PostData格式是长着个样子的:

Node层模拟实现multipart表单的文件上传示例

屏幕快照 2014-11-22 下午9.18.45.png

如图,每一组数据其实就是用“-----WebkitFormBoundary.....”分隔开的,最后再用这个分隔符结束,而且,这个分隔符完全是可自定义的。

每一段提交数据,则通过Content-Disposition来描述,未指定Content-Type,则默认text/plain,如果是上传的二进制文件,指定其mime-type即可。

简单封装一个方法,实现Node层的文件上传:

/**
 * 上传文件
 * @param files  经过formidable处理过的文件
 * @param req  httpRequest对象
 * @param postData 额外提交的数据
 */
function uploadFile(files, req, postData) {
 var boundaryKey = Math.random().toString(16);
 var endData = '\r\n----' + boundaryKey + '--';
 var filesLength = 0, content;

 // 初始数据,把post过来的数据都携带上去
 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);

 // 组装数据
 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));

 // 执行上传
 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 () {
   // 上传成功一个文件之后,把临时文件删了
   fs.unlink(files[key].path);
   uploadedCount++;
   if (uploadedCount == fileNum) {
    // 如果已经是最后一个文件,那就正常结束
    req.end(endData);
   }
  });
  fileStream.pipe(req, {end: false});
 });
}

思路就这样,代码也不复杂,可能额外需要注意的是,在http.request的response处理中,response.headers可能是gzip的,这个时候buffer不能直接toString,需要通过zlib解码再转换为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) {
  // 接口返回数据格式异常,解析失败
  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 的数据,需要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层模拟实现multipart表单的文件上传示例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
IE中createElement需要注意的一个问题
Jul 13 Javascript
jquery中eq和get的区别与使用方法
Apr 14 Javascript
通过jQuery源码学习javascript(三)
Dec 27 Javascript
javascript删除元素节点removeChild()用法实例
May 26 Javascript
JavaScript知识点总结(六)之JavaScript判断变量数据类型
May 31 Javascript
Bootstrap前端开发案例一
Jun 17 Javascript
jQuery实现查找链接文字替换属性的方法
Jun 27 Javascript
基于Bootstrap模态对话框只加载一次 remote 数据的解决方法
Jul 09 Javascript
Vue项目中引入外部文件的方法(css、js、less)
Jul 24 Javascript
使用Angular CLI生成 Angular 5项目教程详解
Mar 18 Javascript
使用svg实现动态时钟效果
Jul 17 Javascript
Vue实现下拉加载更多
May 09 Vue.js
10行原生JS实现文字无缝滚动(超简单)
Jan 02 #Javascript
js原生实现移动端手指滑动轮播图效果的示例
Jan 02 #Javascript
vue父组件向子组件(props)传递数据的方法
Jan 02 #Javascript
基于wordpress的ajax写法详解
Jan 02 #Javascript
基于Vue的SPA动态修改页面title的方法(推荐)
Jan 02 #Javascript
jq.ajax+php+mysql实现关键字模糊查询(示例讲解)
Jan 02 #Javascript
使用async、enterproxy控制并发数量的方法详解
Jan 02 #Javascript
You might like
thinkPHP中session()方法用法详解
2016/12/08 PHP
thinkPHP5.0框架事务处理操作简单示例
2018/09/07 PHP
javascript 使td内容不换行不撑开
2012/11/29 Javascript
table对象中的insertRow与deleteRow使用示例
2014/01/26 Javascript
ECMAScript 5中的属性描述符详解
2015/03/02 Javascript
实例讲解JavaScript的Backbone.js框架中的View视图
2016/05/05 Javascript
canvas知识总结
2017/01/25 Javascript
JS获取今天是本月第几周、本月共几周、本月有多少天、是今年的第几周、是今年的第几天的示例代码
2018/12/05 Javascript
js实现从右往左匀速显示图片(无缝轮播)
2020/06/29 Javascript
对于Python的Django框架部署的一些建议
2015/04/09 Python
详解Python的Django框架中inclusion_tag的使用
2015/07/21 Python
浅析Python中MySQLdb的事务处理功能
2016/09/21 Python
python dict 字典 以及 赋值 引用的一些实例(详解)
2017/01/20 Python
Python实现将罗马数字转换成普通阿拉伯数字的方法
2017/04/19 Python
基于Python开发chrome插件的方法分析
2018/07/07 Python
使用tensorflow实现线性回归
2018/09/08 Python
对django xadmin自定义菜单的实例详解
2019/01/03 Python
Python实现的矩阵转置与矩阵相乘运算示例
2019/03/26 Python
Django组件content-type使用方法详解
2019/07/19 Python
python数组循环处理方法
2019/08/26 Python
Python中zip()函数的简单用法举例
2019/09/02 Python
python科学计算之narray对象用法
2019/11/25 Python
CSS3结构性伪类选择器九种写法
2012/04/18 HTML / CSS
基于 HTML5 WebGL 实现的医疗物流系统
2019/10/08 HTML / CSS
Java的基础面试题附答案
2016/01/10 面试题
优秀教师主要事迹
2014/02/01 职场文书
蜜蜂引路教学反思
2014/02/04 职场文书
保护环境的建议书
2014/03/12 职场文书
《歌唱二小放牛郎》教学反思
2014/04/19 职场文书
结婚当天新郎保证书
2015/05/08 职场文书
婚宴父亲致辞
2015/07/27 职场文书
php 原生分页
2021/04/01 PHP
7个关于Python的经典基础案例
2021/11/07 Python
Redis监控工具RedisInsight安装与使用
2022/03/21 Redis
vue3 自定义图片放大器效果的示例代码
2022/07/23 Vue.js
Go gorilla/sessions库安装使用
2022/08/14 Golang