关于使用 Connect-Busboy 实现文件上传 优化说明
这篇博文完全上关于上一篇的优化 先看上一篇 node.js 在 Express4.0 框架使用 Connect-Busboy 实现文件上传 因为从上次博客改用 connect-busboy 来上传文件后,发现了明显的一个bug
bug 说明
文件显示上传 100% ,然后预览的时候,偶尔会发现图片只能显示一部分 这种情况在 png 格式 图片尤其严重 昨天重新 review 代码,发现一个bug ,当然和 connect-busboy 一点关系都没有,而是涉及到流的处理过程.
这里把上一篇 blog 里贴出的上传代码在放上来分析一下
function upload(req, res, next) {
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
var tmp_path=path.join(os.tmpDir(), path.basename(filename));
file.pipe(fs.createWriteStream(tmp_path));
file.on('end',function(){
var uuid = tool.generateUUID();
commfile.savePathFile(uuid, mimetype, tmp_path, true, function(err, fileBase64) {
if (err) {
res.json({
success:false,
url:''
});
}else{
res.json({
success: true,
url: '/file/' + uuid
});
}
});
});
});
req.pipe(req.busboy);
}
针对代码,我们细致理一下 req.busboy.on('file' ,function(......).....)
1 .req.busboy.on 方法用来监听 form 提交的参数的事件,其中 on(‘file’ ) 是监听 form 提交二进制文件流事件. 上面的监听事件的回调函数传递过来几个参数,我们重点看下 file 参数 file 是二进制文件流句柄,它是一个标准的 stream 2 .接着我取到服务器临时文件目录 ,和 文件名,组合成一个文件路径
类似这样的路径: /Users/zhangzhi/data/temp/aaa.png
var tmp_path=path.join(os.tmpDir(), path.basename(filename));
3 .接着我把 file 流写入上面定义的文件路径
file.pipe(fs.createWriteStream(tmp_path));
- 这里出现了一个流程冗余问题
上面用到了 pipe 希望读一下我的另外一篇文章了解 pipe 原理 node.js 在io实现异步非阻塞
- 然后我读取刚才的临时文件到字节数组,转化为 base64 字符串,存储在了 LevelDB数据库
file.on('end' ....)
- bug 就出现在这里
我们回头分析一下上面出现的流程冗余问题及bug
流程冗余
假如我的图片是直接保存在服务器的某个文件夹下面,那么上面的 1,2,3 流程是没有问题,(当然不需要临时文件目录,直接写入到服务器指定的保存图片目录即可)
但是我目前的博客图片全部在 levelDB 数据库里,所以没有必要先在临时目录生成图片,然后读取图片字节数组,再转化为base64串写入数据库
中间的弯路就是保存临时文件再读取我们已经拿到了 file 上传文件流句柄,所以完全可以直接塞到二进制数组中,临时保存,毕竟不是上面巨大文件,对内存容忍度为0 ,如下代码
var dataArr = [];
file.on("data", function (chunk){
dataArr.push(chunk);
});
上面我们监听 file 的 data 事件,通过源源不断的字节流传输到服务器,我们就不管三七二十一先塞进 dataArr 二进制数组中 那么接着我们要做什么,我们直接可以把上面的 dataArr 数据转化成一个 base64 字符串存入数据库,具体的代码实现在下面,我们该分析下上面的bug了
bug出现
我们上面说到了第 4 步有一个隐形的bug ,在上传文件的时候偶尔出现,尤其是 png 图片时,100% 暴露,叔能忍,婶婶也忍不了啊… 为什么第 4 步这里有bug ? 还是脱离不了整个流程,简单回顾下.
上传的文件流 file 写入到临时文件目录中 ,假设这个过程叫 A 我们读取 临时文件 文件转化为 base64 字符串, 这个过程叫 B 我们应该什么时候去读取 临时文件,应该在 A 过程全部结束, 而 A 过程结束是指 file 上传文件流全部传完 并且 文件流全部写入到了临时文件 但是我们上面 监听的 file.on('end’,function(){…} 只是监听上传文件流结束的事件,具体这个文件流是否完全写入到临时文件,根本不知道,但是这时我们已经做了一个错误的决定,就是开始读取 临时文件 转化为base64 字符串 所以: 当文件流仅仅是通过 http 传完时,但是并没有写完到临时文件,这是我们立即读取图片,一定是不完整的图片
如果这里一定要这样去做,改如果修改一下代码呢:
function upload(req, res, next) {
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
var tmp_path=path.join(os.tmpDir(), path.basename(filename));
var passedLength = 0;
var writeStream = fs.createWriteStream(tmp_path);
file.on('data',function(trunk){
passedLength += chunk.length;
if (writeStream.write(chunk) === false) {
file.pause();
}
});
file.on('end',function(){
//当没有数据读取,关闭写入数据流
writeStream.end();
});
writeStream.on('drain', function() {
//速度写入完毕后
//这里我们才去执行把图片文件转换成 base64 字符串写入数据库操作
var uuid = tool.generateUUID();
commfile.savePathFile(uuid, mimetype, tmp_path, true, function(err, fileBase64) {
if (err) {
res.json({
success:false,
url:''
});
}else{
res.json({
success: true,
url: '/file/' + uuid
});
}
});
});
req.pipe(req.busboy);
}
上面的代码是没有经过测试的,直接敲上去,只是为了说明按照原来流程的正确处理方式
流程问题理清了,bug 也找到了.下面做如下修复
先上代码
req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
var dataArr = [], len = 0,strBase64;
file.on("data", function (chunk){
dataArr.push(chunk);
len += chunk.length;
});
file.on("end", function () {
strBase64 = Buffer.concat( dataArr, len ).toString('base64');
var uuid=tool.generateUUID();
commfile.saveFile(uuid,mimetype,strBase64,function(err){
if (err) {
res.json({
success:false,
url:''
});
}else{
res.json({
success: true,
url: '/file/' + uuid
});
}
})
});
});
req.pipe(req.busboy);
简单说明一下上面的代码 file.on(“data” 事件实时把字节流写入数组中 file.on(“end” 当上传文件字节流结束后, 把字节数组转化为 base64 字符串 strBase64 = Buffer.concat( dataArr, len ).toString(‘base64’); 最后我们把 base64 字符串写入数据库 这样上传图片再也不会出现写入数据库的图片不完整的bug 了.
关于使用 Connect-Busboy 实现文件上传 优化说明的更多相关文章
- springMVC3学习(十二)--文件上传优化CommonsMultipartResolver
基于上一篇文件上传发现效率很慢,我们应该对它进行优化 使用springMVC对文件上传的解析器 来处理文件上传的时候需要在spring的applicationContext里面加上springMVC ...
- springMVC文件上传优化
1. 新建web project 2. 填 jar,注意新加入两个上传文件的jar, commons-io, commons-fileupload 3. 改写web.xml <?xml vers ...
- Java web文件上传下载
[版权申明:本文系作者原创,转载请注明出处] 文章出处:http://blog.csdn.net/sdksdk0/article/details/52048666 作者:朱培 ID:sdksdk0 邮 ...
- Java FtpClient 实现文件上传服务
一.Ubuntu 安装 Vsftpd 服务 1.安装 sudo apt-get install vsftpd 2.添加用户(uftp) sudo useradd -d /home/uftp -s /b ...
- JAVA中使用FTPClient实现文件上传下载实例代码
一.上传文件 原理就不介绍了,大家直接看代码吧 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ...
- 【C#公共帮助类】FTPClientHelper帮助类,实现文件上传,目录操作,下载等动作
关于本文档的说明 本文档使用Socket通信方式来实现ftp文件的上传下载等命令的执行 欢迎传播分享,必须保持原作者的信息,但禁止将该文档直接用于商业盈利. 本人自从几年前走上编程之路,一直致力于收集 ...
- 【FTP】FTP文件上传下载-支持断点续传
Jar包:apache的commons-net包: 支持断点续传 支持进度监控(有时出不来,搞不清原因) 相关知识点 编码格式: UTF-8等; 文件类型: 包括[BINARY_FILE_TYPE(常 ...
- 【SFTP】使用Jsch实现Sftp文件上传-支持断点续传和进程监控
JSch是Java Secure Channel的缩写.JSch是一个SSH2的纯Java实现.它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等,当然你也可以集成它的功能到 ...
- 【转】JSch - Java实现的SFTP(文件上传详解篇)
JSch是Java Secure Channel的缩写.JSch是一个SSH2的纯Java实现.它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等,当然你也可以集成它的功能到 ...
随机推荐
- Android开发-API指南-<data>
<data> 英文原文:http://developer.android.com/guide/topics/manifest/data-element.html 采集(更新)日期:2014 ...
- Flex 中文字体终极解决方案
一直以来Flash对中文的支持就不是很好,很多人都发现很多汉字在Flex中无法设置粗体,就是其中一个表现,经过一晚上的折腾,终于突破了这个难题,其实,答案就在Adobe的官方教程里,只能怪自己英文水平 ...
- 使用UI Automation实现自动化测试 --微软提供的控件Pattern
微软提供的控件Pattern System.Windows.Automation 命名空间 System.Windows.Automation.BasePattern 为控件模式类提供基实现 Syst ...
- 【HTML/XML 10】XML文档中的Schema文件
导读:DTD是对XML文档进行有效性验证的方法之一,事实上,继DTD之后,出现了用来规范和描述XML文档的第二代标准:Schema.Schema是DTD的继承,但是也有其不同的地方,它是真正的以独立的 ...
- SVN与TortoiseSVN实战:冲突详解(一)
硬广:<SVN与TortoiseSVN实战>系列已经写了三篇,第一篇<SVN与TortoiseSVN实战:从入门到精通>,第二篇<SVN与TortoiseSVN实战:标签 ...
- 理解python可变类型vs不可变类型,深拷贝vs浅拷贝
核心提示: 可变类型 Vs 不可变类型 可变类型(mutable):列表,字典 不可变类型(unmutable):数字,字符串,元组 这里的可变不可变,是指内存中的那块内容(value)是否可以被改变 ...
- 查找Safari相关迹证
日前有取证的同好提及Safari,想了解详细步骤,因而在此再补充说明相关. 除了Winodws外,Mac OS X也有为数不少的使用者,以下便以OS X自带的Safari浏览器为例,来查看有哪些重要迹 ...
- Secure your iPhone with 6 digit passcode by upgrading to iOS9
IP-Box could crack 4 digit passcode, what about 6 digit passcode??? All you need to do is to upgrade ...
- Android IOS WebRTC 音视频开发总结(六三)-- 2016国内IM云服务行业分析
本文主要国内IM云服务行业分析,文章最早发表在我们的微信公众号上,详见这里,欢迎关注微信公众号blackerteam,更多详见www.blackerteam.com 谈到IM我们最先想到的是qq和微信 ...
- Centos更新yum packet源
在使用Centos时,常常会遇到使用yum安装某些系统依赖包,特别是第三方软件库(如openstack软件库)时,无法找到包源.因此,需要将Centos的yum源进行更新,扩展,以便可以通过yum的方 ...