在node.js下浅谈文件上传的方法
2016-06-28 14:53
821 查看
这是接着上篇文章的下部分,关于文件上传的方法。相对下载,前端部分复杂了不少,接下来就谈谈上传的问题。
用法如下:
这样一套下来带走表单数据。这是我们前端上传的核心部分之一。
该模块使用前需要初始化,设置一些数据参数。
首先是上传文件保存的地址:
这个文件地址用于存储文件上传后放置的文件夹位置,系统会自动给它命名。
之后需要提前设置通过multipart/form-data上传过来的文件的name属性。比如我通过FormData封装了三个数据,分别是”username”,”id”,”file”。那么,我需要在multer的实例中设置这三个数据,代码如下:
之后在router中传入这个中间件,后台就可以接收到从uploadFile接口传入的数据:
文件的数据保存在req.files.file中,其他类型数据保存在req.body.dir中。
单文件多ajax由于一次只有一个文件,报文size相对比较小,相对稳定性比较好。但是由于对服务器重复请求增加了时间总长度,总上传时间没有只进行一次ajax快。
大致代码如下:
多文件少ajax由于有更少的请求,总时间比单文件ajax流快。但由于报文由几个文件叠加,几个大文件叠加起来的总报文会非常大,网络不稳定时可能会导致上传了很久的文件最后功亏一篑。
由于FormData可以装入多个文件名一致的文件,所以代码如下:
具体选择哪种传输见仁见智。在这里我用的是单文件多ajax的方式。
值得一提的是,submit的提交方式,在多选文件的情况下是提交一组文件的,也就是多文件单ajax的方式。
在多个ajax成功上传后,控制台只会输出并不对应数量的callback甚至只有一个callback,前几个都没能顺利的进去,我不能肯定准确的原因。在研究jquery源码后发现,这里需要简单的将XMLHttpRequest一系列步奏封装成函数,就可以顺利走进每个回调了。
不知什么原因让浏览器遗忘了这些callback,用数组保存了这些xhr和callback,response该返回的也都返回了,或许需要闭包才能好好的保存它们。有知道原因的可以告诉我一下。
上面写的有点不够好,参考jquery,xhr再封装一下。
之后只需调用xhrSend(options)即可。
接下来使用Promise.all控制所有rename:
至此,所有步骤完成。完整demo在这里
Html5上传
-FormData
FormData是html5新提供XMLHttpRequest Level 2 api,对post数据做了很方便的改进,最大的优点就是可以封入文件类型的对象,上传混合数据时不用依赖submit方法了。用法如下:
var formData = new FormData(); formData.append('username', '张三'); formData.append('id', 123456); formData.append('file', file); //file为通过file input选择的文件obj var xhr = new XMLHttpRequest(); xhr.open("post", url); xhr.send(formData);
这样一套下来带走表单数据。这是我们前端上传的核心部分之一。
-multer模块
在express中,上传multipart/form-data数据需要使用multer模块,一个express的中间件服务。该模块使用前需要初始化,设置一些数据参数。
首先是上传文件保存的地址:
var multer = require('multer'); var upload = multer({ dest: './uploads/'}); //保存在uploads文件夹中
这个文件地址用于存储文件上传后放置的文件夹位置,系统会自动给它命名。
之后需要提前设置通过multipart/form-data上传过来的文件的name属性。比如我通过FormData封装了三个数据,分别是”username”,”id”,”file”。那么,我需要在multer的实例中设置这三个数据,代码如下:
var cpUpload = upload.fields([ {username: 'file'}, {id: 'file'}, {file: 'src', maxCount: 10} //maxCount为一次传输文件的最大数量 ]);
之后在router中传入这个中间件,后台就可以接收到从uploadFile接口传入的数据:
router.post("/uploadFile",cpUpload, function(req, res, next){ var files = req.files.file, dir = req.body.dir; ... });
文件的数据保存在req.files.file中,其他类型数据保存在req.body.dir中。
-上传并发数量
和下载类似的,上传存在上传单个文件和多个文件的区别。因此上传思路可以有两种:单文件多ajax,多文件少ajax。单文件多ajax由于一次只有一个文件,报文size相对比较小,相对稳定性比较好。但是由于对服务器重复请求增加了时间总长度,总上传时间没有只进行一次ajax快。
大致代码如下:
for (var key in files) { if (parseInt(key,10) >= 0) { //排除length等不需要的属性 var formData = new FormData(); formData.append("file",files[key]); ... //xhr的初始化等代码 xhr.send(formData); } }
多文件少ajax由于有更少的请求,总时间比单文件ajax流快。但由于报文由几个文件叠加,几个大文件叠加起来的总报文会非常大,网络不稳定时可能会导致上传了很久的文件最后功亏一篑。
由于FormData可以装入多个文件名一致的文件,所以代码如下:
var formData = new FormData(); for (var key in files) { if (parseInt(key,10) >= 0) { //排除length等不需要的属性 formData.append("file",files[key]); } } ... //xhr的初始化等代码 xhr.send(formData);
具体选择哪种传输见仁见智。在这里我用的是单文件多ajax的方式。
-低版本ie兼容
低版本ie下不能使用FormData的方式传输文件,需要用原来的submit方式。这里直接贴代码。var form = document.getElementById("form"); var iframe = document.getElementById("iframe"); if(!iframe){ iframe = document.createElement("iframe"); } form.action = "/uploadFile"; form.target = "iframe"; iframe.name = "iframe"; iframe.src = ""; iframe.style.display = "none"; iframe.onload = function(){ ... }; document.getElementById("options").appendChild(iframe); form.submit();
值得一提的是,submit的提交方式,在多选文件的情况下是提交一组文件的,也就是多文件单ajax的方式。
ajax回调
-ajax回调
在单文件多ajax的时候,可能会遇到多个ajax回调的问题。在一般情况下,如果直接按照正常的方式写ajax回调会出现回调被覆盖的问题。for (var key in files) { if (parseInt(key,10) >= 0) { //排除length等不需要的属性 var formData = new FormData(); formData.append("file",files[key]); formData.append("dir",rootDir); var xhr = new XMLHttpRequest(); xhr.open("POST","/uploadFile"); xhr.send(formData); //并不是每个ajax的onreadystatechange都会触发 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ xhr.onreadystatechange = function(){}; if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ console.log("succ"); }else{ console.log("error"); } } } } }
在多个ajax成功上传后,控制台只会输出并不对应数量的callback甚至只有一个callback,前几个都没能顺利的进去,我不能肯定准确的原因。在研究jquery源码后发现,这里需要简单的将XMLHttpRequest一系列步奏封装成函数,就可以顺利走进每个回调了。
for (var key in files) { if (parseInt(key,10) >= 0) { //排除length等不需要的属性 var formData = new FormData(); formData.append("file",files[key]); formData.append("dir",rootDir); xhrTest(); function xhrTest(){ var xhr = new XMLHttpRequest(); xhr.open("POST","/uploadFile"); xhr.send(formData); //正常回调 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ xhr.onreadystatechange = function(){}; if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ console.log("succ"); }else{ console.log("error"); } } }; } } }
不知什么原因让浏览器遗忘了这些callback,用数组保存了这些xhr和callback,response该返回的也都返回了,或许需要闭包才能好好的保存它们。有知道原因的可以告诉我一下。
上面写的有点不够好,参考jquery,xhr再封装一下。
var xhrId = 0; var xhrCallbacks = {}; var xhrCallbackCount = 0; /** * XMLHttpRequest的封装 * @params options 参考jquery.ajax */ function xhrSend(options){ var callback; return (function(){ var xhr = new XMLHttpRequest(); var id = ++xhrId; xhr.open(options.method,options.url); xhr.send(options.data); callback = function(){ if(xhr.readyState == 4){ delete xhrCallbacks[id]; xhrCallbackCount--; xhr.onreadystatechange = function(){}; if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){ options.success && options.success(xhr.responseText); }else{ options.error && options.error(xhr.responseText); } if(xhrCallbackCount == 0){ options.done && options.done(); } } } xhrCallbackCount++; xhr.onreadystatechange = xhrCallbacks[id] = callback; })(); }
之后只需调用xhrSend(options)即可。
上传到当前目录
事实上由于在multer模块的设置,上传的文件只在当前的uploads目录下。但是由于我们能获取到文件名,因此只要使用fs模块的rename移动文件并重命名就可以了。fs.rename(currdir,realdir,function(err){ if(err){} });
-用promise处理异步回调
由于fs.rename是异步,在多个fs.rename异步下不知道它何时完成所有的回调任务。这里使用es6的promise控制异步回调。var fsPromise = function(file){ return new Promise(function(resolved,rejected){ fs.rename(currdir,realdir,function(err){ if(err){ rejected(err); }else{ resolved(); } }); }); }
接下来使用Promise.all控制所有rename:
Promise.all(files.map(fsPromise)) .then(function(){ res.set({ 'Content-Type':'text/html' }); res.send({"code":"s_ok"); }) .catch(function(err) { res.send({"code":"failed", "summary":err}); });
至此,所有步骤完成。完整demo在这里
相关文章推荐
- Hadoop datanode 无法启动,报错:java.io.IOException: All specified directories are failed to load.
- Node.js 获取客户端IP
- 写给 Node.js 学徒的 7 个建议
- Node.js 初学者的入门资源汇总
- [24] Swap Nodes in Pairs
- leetcode 24. Swap Nodes in Pairs
- Hadoop NameNode HA实现解析
- nodejs中获取时间戳、时间差
- 使用 superagent 与 cheerio 完成简单爬虫
- 《Nodejs开发加密货币》之十九:签名和多重签名
- hadoop 第二节 单节点集群配置 Setting up a Single Node Cluster
- (4)文件类型和扩展名,node和block
- NodeJs——(9)jade的基础和进阶
- 10+ 最佳的 Node.js 教程和实例
- nodejs教程之环境安装及运行
- 237. Delete Node in a Linked List
- nodejs-express环境搭建
- kafka broker 进入 conflicted ephemeral node 死循环
- 异步I/O原理
- node.js 项目相关