利用node.js制作ajax上传文件进度提示

文件上传在互联网应用中非常常见,比如上传一个头像之类的。一般想要提高用户体验需要告诉用户当前上传的文件进度,比如“50%”等信息,特别是在用户网络比较慢或是文件比较大的情况下。

而javascript在这方面可以借助ActiveX来获取客户端文件信息,但是其兼容性不佳,火狐,Safari等浏览器是不兼容ActiveX,于是乎网上都是清一色的借助flash的头像上传插件。

这可苦了我们这一类不会flash的开发者,但是办法总比困难多,想要达到和flash一样的头像上传进度显示也并非不可能,下面我来介绍一下后端借助node.js开发的一个ajax文件上传模型,当然是带进度显示。

之前的node.js网络聊天室头像就用到了这个功能,先上几张截图把:

u1

1、上传失败,图片格式错误,我传了一个.RAR文件

u2

2、上传中1,当然如果图片很小的话,这里几乎是看不到进度条改变的

u3

3、上传中2,这里可以根据需要做成自己喜欢的样子,比如进度条

u4

4、上传成功,会将默认头像替换成新的

u5

5、由于测试,所以没有设置图片超大,一共发送了18个请求获取上传信息,完成上传。

先说前端代码:

首先完全利用ajax也就是XMLHTTPRequest来上传文件是不可能的,网上流传的一些ajax上传文件无非分两种,一个是借助ActiveX,另一个是借助隐藏的iframe。由于前者的局限性,我们选择使用后者。

在页面中插入一个form表单和iframe:

注:本文只在说明工作原理,示例代码是部分或删减,不能直接运行!

1
2
3
4
5
6
7
8
<form id="upload_form" action="/upload"  target="face_upload" method="post" enctype="multipart/form-data">
<input name="random" id="random" value="1314414282450" type="hidden">
<input type="file" name="face" id="face"/>
<button id="login" type="submit">上传</button>
<img src="" width="50" height="50" id="i_f">
<div id="progress_div"></div>
</form>
<iframe name="face_upload" id="face_upload" src="" style="display: none"></iframe>

代码很简单,一个form表单,提交到下面那个iframe中去,这里要特别说明一下random这个input,是从后端传过来的唯一ID,由用户ID+时间戳生成。

前端js代码就不贴了,只是说下工作原理:

1、用户选择文件后,按上传提交表单,实际上是提交到下面的iframe中去,提交内容还将包括“random”随机数以及回调函数名和操作成功参数。注:回调函数名和成功参数需要在前端准备好,例如:

1
2
3
4
5
6
7
var uploadcallback = function(err){
if(err){
alert('图片格式错误');
return false;
}
alert(‘上传成功');
}

2、等用户提交后程序挂起500毫秒,然后就发送random这个随机数到后台,(注:这里的ajax请求需要加上随机数防止被浏览器缓存。),收到返回后,隔1000毫秒再次发去请求。(注:这里为了兼容IE和火狐的onreadystatechange,否则只需发一次请求)

3、当node.js服务端接收到random随机数和请求后,将当前随机数代表的文件上传信息返回给前台。信息包括文件总大小,上传了多少。

4、前台根据第3步接收到的数据经过计算或是其他表现形式展现给用户。

5、当文件上传结束时,后端返回一个“end”的标识,前端收到这个标识后则不再发送ajax请求文件上传情况,告诉用户上传成功。

以上是正常流程,具体前端工作流程图如下:

u6

然后是后端node.js:

可以用redis或用户ID+时间戳生成一个唯一随机码,在用户请求页面时放入用户页面的一个隐藏input中,是否设置超时看具体情况。

1、首先接收用户form表单提交过来的内容先经过nginx检查是否超大,这里的超大可能是整个站点上传文件最大值,具体是否超过大小需要进入到下面的node.js模块去验证。类似第一道大门。

2、根据随机数去一个全局变量内创建对象来记录和跟踪这个文件的上传信息

3、在node.js端加载模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
formidable = require('formidable');  
//加载 formidable 模块,具体 formidable 模块的安装和运用请参阅:https://github.com/felixge/node-formidable/
var form = new formidable.IncomingForm();
//实例化formidable的IncomingForm这个类,这样就可以用form的一些方法和属性了
var count = 0;
form
.on('progress', function(bytesReceived, bytesExpected ,ending){
count++;
if(count%50 === 0){ //这个50是随便你设置的,可以根据前端发送AJAX获取文件信息的频率来设置
//do something 存入文件上传信息对象,以便用户取得
//bytesReceived表示接收到字节数,bytesExpected 表示总字节数
}
})

这样就可以在每次接收到字节数时根据一定的策略在随机ID对象内写入和更新文件上传的信息。

4、当上传完成后,node.js再写一个函数来让前端js发送过来的回调函数执行,并且对随机ID的对象写入上传完毕的信息。接着转存文件然后删除临时文件。这里可以根据需要给前端回调函数返回需要的文件信息,例如存放路径、文件格式、文件真实大小。

1
2
3
4
5
6
upload.res_callback = function(func_name, param){
var str = '<script>top.'+func_name+'('+param+');</script>'
return str;
}
var str = upload.res_callback (func_name, param);
res.end(str);

这样就可以让前端执行这个回调了。

5、当ajax请求过来,返回end信号后,node.js将回收这个存储随机ID的文件信息对象。

以上是正常流程,就是不出错的流程,下面是流程图:

u7

最后说一个小方法,可以根据文件头来判断文件是否是图片或是其他,具体文件头对应关系请查阅相关资料。

写文章真累,整个前后端开发不过花了半天时间,写这个文章竟然用了一天。。。哈哈!