关于使用 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转发,文件传输等,当然你也可以集成它的功能到 ...
随机推荐
- openGL 提升渲染性能 之 顶点数组 VBO IBO VAO
使用openGL图形库绘制,都需要通过openGL接口向图像显卡提交顶点数据,显卡根据提交的数据绘制出相应的图形. openGL绘制方式有:直接模式,显示列表,顶点数组,顶点索引. 直接模式:最简单, ...
- 华为OJ平台——统计字符串中的大写字母
题目描述: 统计字符串中的大写字母的个数 输入: 一行字符串 输出: 字符串中大写字母的个数(当空串时输出0) 思路: 这一题很简单,直接判断字符串中的每一个字符即可,唯一要注意的一点是输入的字符串可 ...
- Edit 方法
1. 在FORM 的grid里面作为记录选择字段 AX 的edit 方法可以很方便地给用户提供记录选择功能,而不用在TABLE上添加新字段. 通常结合map使用,一般edit 方法格式: edit N ...
- css选择器分类
css选择器大致可以分为10大类: 1.元素选择器 如:p{} 2.类选择器 如:.xx{} 3.ID选择器 如:#xx{} 4.关联选择器 如:p a{} 5.子元素选择器 如:p>a{} 6 ...
- HTML中解决双击会选中文本的问题
HTML中解决双击会选中文本的问题 <div unselectable="on" style="-moz-user-select:none;" onsel ...
- hdu1505
the main algorithm as the 1506 #include <stdio.h> #include <iostream> #include <strin ...
- 014安装Linux系统到开发板
SD卡----->开发板 1.安装准备: 硬件连接 USB下载线,一端连到开发板,另一端连到PC机: 串口线连好: 电源线连好: 设置开发板从SD卡启动: 2.打开开发板进入选单界面: 进入选单 ...
- 捣蛋phpwind之WindFrameWork
一直都有关注phpwind这个开源产品,从9.0开始就好关注拉,因为官方说把之前的代码重写了一遍,融入了windFramework这个框架,代码真的挺优美的,今日在做社区的一些功能,心血来潮就参考了p ...
- C#基础-可访问性-public、ptotected、protected internal、internal、private
1.类型的可访问性 a.关键字有public.internal,其中public表示该类型对于程序集都可见.internal表示该类型仅对当前程序集可见.此处的类型可以为类.接口.结构等. b.如果不 ...
- Wpf实现图片自动轮播自定义控件
近来,公司项目需要,需要写一个自定义控件,然后就有下面的控件产生.样式没有定义好,基本功能已经实现.1.创建为自定义控件的XAML页面.下面为后台代码 using System; using Syst ...