Node.js实现文件上传


Posted in Javascript onJuly 05, 2016

在工作中碰到了这样的需求,需要用nodejs 来上传文件,之前也只是知道怎么通过浏览器来上传文件, 用nodejs的话, 相当于模拟浏览器的行为。 google 了一番之后, 明白了浏览器无非就是利用http协议来给服务器传输数据, 具体协议就是《RFC 1867 - Form-based File Upload in HTML》, 在浏览器上通过form 表单来上传文件就是通过这个协议,我们可以先看看浏览器给服务端发送了什么数据, 就可以依葫芦画瓢的把上传功能实现出来。说起form 表单上传文件的话, 大家应该很熟悉:

<form action="http://www.qq.com/" method="post">
<input type="text" name="text1" /><br />
<input type="text" name="text2" /><br />
<input type="submit" />
</form>

提交时, 用fiddler 抓包可以看到向服务端发出这样的数据:

POST http://www.qq.com/ HTTP/1.1
Host: www.qq.com
Content-Length: 23
Content-Type: application/x-www-form-urlencoded; charset=UTF-8

text1=hello&text2=world

值得注意的是Content-Type默认为application/x-www-form-urlencoded,所以消息会经过URL编码。比如“你好”会编码为 %E4%BD%A0%E5%A5%BD。

接下来我们看一下通过form 表单是怎么上传的。大家应该也不陌生:

<form action="http://www.qq.com" method="post" enctype="multipart/form-data">
<input type="file" name="myfile" />
<input type="submit" value="submit" />
</form>

然后新建一个只有hello world字样的upload.txt文本文件上传上去,我们再吃用fiddler 来抓下包, 可以发现发送过去的数据稍微复杂了一些(已经去掉了很多的其它没关系的请求行,比如缓存控制和cookie之类的):

POST http://www.qq.com/ HTTP/1.1
Host: www.qq.com
Content-Length: 199
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G

------WebKitFormBoundarywr3X7sXBYQQ4ZF5G
Content-Disposition: form-data; name="myfile"; filename="upload.txt"
Content-Type: text/plain

hello world

------WebKitFormBoundarywr3X7sXBYQQ4ZF5G--

根据RFC 1867的定义,我们需要生成一段边界数据,这个数据不能在内容的其它地方出现,这个可以自己定义, 在每个浏览器的生成算法可能都不一样, 上面的boundary就是分隔数据,生成了分隔数据之后, 就可以把分隔数据放在头部的Content-Type里面传送给服务端, 也就是上文的 Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G, 另外,上传的内容,需要用分隔数据来分隔成若干个段,然后每段数据里面都有文件的文件名,还有上传时候的name,服务端就是用这个name来接收文件,还有文件的类型Content-Type,在这个例子里是 text/plain,如果上传的是png图片就是image/png。文件类型的一个空行后就是所上传的文件的内容,在这个例子里也是为了容易理解所以上传的是文本文件所以内容直接就能够显示出来,如果上传的是图片文件, 因为是二进制文件,fiddler 就显示的是乱码。 文件的内容结束之后就是一个空行再加上边界数据。

了解了发送格式的细节之后, 下一步就是使用nodejs来编程实现,简单来讲, 就是按照格式把数据发送给服务端就行了。

const http = require('http');
const fs = require('fs');
//post地址为本地服务的一个php,用于测试上传是否成功
var options = {
hostname: 'localhost',
port: 80,
path: '/get.php',
method: 'POST'
}
//生成分隔数据
var boundaryKey = '----WebKitFormBoundaryjLVkbqXtIi0YGpaB'; 
//读取需要上传的文件内容
fs.readFile('./upload.txt', function (err, data) {
//拼装分隔数据段
var payload = '--' + boundaryKey + '\r\n' + 'Content-Disposition:form-data; name="myfile"; filename="upload.txt"\r\n' + 'Content-Type:text/plain\r\n\r\n';
payload += data;
payload += '\r\n--' + boundaryKey + '--';
//发送请求
var req = http.request(options, function (res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('body:' + chunk);
});
});
req.on('error', function(e) {
console.error("error:"+e);
});
//把boundary、要发送的数据大小以及数据本身写进请求
req.setHeader('Content-Type', 'multipart/form-data; boundary='+boundaryKey+'');
req.setHeader('Content-Length', Buffer.byteLength(payload, 'utf8'));
req.write(payload);
req.end();
});

本文重点在于了解协议并且用代码实现出来, 代码组织上面还有很多优化的地方。

最后在本地apache,简单写一个php来保存上传的文件来用作测试:

<?php
$filePath = './upload.txt';
move_uploaded_file($_FILES['myfile']['tmp_name'] , $filePath);
echo "ok";
?>

另外,根据RFC 1867 还可以实现一次上传多个文件的功能, 这个在这里就不详述, 需要的话可以详细参考RFC 1867来实现。

以上所述是小编给大家介绍的Node.js实现文件上传,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Javascript中的Callback方法浅析
Mar 15 Javascript
详解前端构建工具gulpjs的使用介绍及技巧
Jan 19 Javascript
微信小程序 弹框和模态框实现代码
Mar 10 Javascript
cocos creator Touch事件应用(触控选择多个子节点的实例)
Sep 10 Javascript
JavaScript多线程运行库Nexus.js详解
Dec 22 Javascript
通过实例解析js简易模块加载器
Jun 17 Javascript
vue router总结 $router和$route及router与 router与route区别
Jul 05 Javascript
微信小程序点击生成朋友圈分享图(遇到的坑)
Jun 17 Javascript
基于VUE实现判断设备是PC还是移动端
Jul 03 Javascript
VUE使用 wx-open-launch-app 组件开发微信打开APP功能
Aug 11 Javascript
详解vue 组件的实现原理
Nov 12 Javascript
vue+element UI实现树形表格
Dec 29 Vue.js
JavaScript数组方法大全(推荐)
Jul 05 #Javascript
JS与HTML结合使用marquee标签实现无缝滚动效果代码
Jul 05 #Javascript
js利用正则表达式检验输入内容是否为网址
Jul 05 #Javascript
后端接收不到AngularJs中$http.post发送的数据原因分析及解决办法
Jul 05 #Javascript
微信JS-SDK坐标位置如何转换为百度地图坐标
Jul 04 #Javascript
Bootstrap实现水平排列的表单
Jul 04 #Javascript
JSONP跨域请求实例详解
Jul 04 #Javascript
You might like
PHP获取网址的顶级域名函数代码
2012/09/24 PHP
PHP垃圾回收机制引用计数器概念分析
2013/06/24 PHP
深入分析PHP优化及注意事项
2016/07/04 PHP
Extjs gridpanel 出现横向滚动条问题的解决方法
2011/07/04 Javascript
《JavaScript高级程序设计》阅读笔记(二) ECMAScript中的原始类型
2012/02/27 Javascript
jqgrid 编辑添加功能详细解析
2013/11/08 Javascript
jQuery页面加载初始化常用的三种方法
2014/06/04 Javascript
jquery阻止后续事件只执行第一个事件
2014/07/24 Javascript
微信小程序使用第三方库Immutable.js实例详解
2016/09/27 Javascript
Bootstrap CSS组件之大屏幕展播
2016/12/17 Javascript
微信小程序 中wx.chooseAddress(OBJECT)实例详解
2017/03/31 Javascript
vue2.0 移动端实现下拉刷新和上拉加载更多的示例
2018/04/23 Javascript
vue 巧用过渡效果(小结)
2018/09/22 Javascript
clipboard在vue中的使用的方法示例
2018/10/19 Javascript
基于JS开发微信网页录音功能的实例代码
2019/04/30 Javascript
js getBoundingClientRect使用方法详解
2019/07/17 Javascript
javascript中undefined的本质解析
2019/07/31 Javascript
layui监听单元格编辑前后交互的例子
2019/09/16 Javascript
适用于 Vue 的播放器组件Vue-Video-Player操作
2020/11/16 Javascript
[01:11]回顾历届DOTA2国际邀请赛中国区预选赛
2017/06/26 DOTA
Python编写的com组件发生R6034错误的原因与解决办法
2013/04/01 Python
跟老齐学Python之永远强大的函数
2014/09/14 Python
Python wxPython库Core组件BoxSizer用法示例
2018/09/03 Python
python3正则提取字符串里的中文实例
2019/01/31 Python
Python分布式进程中你会遇到的问题解析
2019/05/28 Python
在Python中预先初始化列表内容和长度的实现
2019/11/28 Python
解决在keras中使用model.save()函数保存模型失败的问题
2020/05/21 Python
Python配置pip国内镜像源的实现
2020/08/20 Python
浅谈html5增强的页面元素
2016/06/14 HTML / CSS
Boden美国官网:英伦原创时装品牌
2017/07/03 全球购物
美国经典刺绣和字母儿童服装特卖:Smocked Auctions
2018/07/16 全球购物
爱尔兰电脑、家电和家具购物网站:Buy It Direct
2019/07/09 全球购物
综合实践活动方案
2014/02/14 职场文书
大学生评语大全
2014/04/18 职场文书
高中同学会致辞
2015/08/01 职场文书
springboot中的pom文件 project报错问题
2022/01/18 Java/Android