国庆节对于一个大三党来说是一整块难得的时间,出去玩也好,做其他的事也好,都可以随心所欲地安排。想着利用这个时间开发 一个社交网站作为学习node的一个总结,不怎么精,也算凑合,无奈还有太多的事儿要做啊。废话不多说,转入正题:
今天遇到图片上传的问题,服务器能成功收到图片信息,但如何将进度返给客户端呢?用 <form method='post' action="" target="hidden- iframe" enctype='multipart/form-data'></form>发送文件是一个post请求,服务器只可能回复一次,而且这个回复主要用来回复前端上传 文件的获取地址,不适合再做他用。
网上查了查,说是可以用flash做,好吧,表示不会flash… 目前主要的问题是如何不断地从服务器获得上传进度,socket貌似能做到这一点,问题是不知道上传结束时客户端如何主动断开socket链接,有知道的朋友麻烦告诉一声^-^,于是决定采用轮询了。 先贴前端部分代码: html部分
<iframe name="hidden_iframe" id='hidden_iframe' style="display:none" src=""> </iframe>
<form method='post' action='/uploadimage' enctype='multipart/form-data' target="hidden_iframe">
<input type="text" name="randomID" id="uploadTag" style="display:none" />
选择图片:<input type='file' name='img' id='filearea' onchange="setTag()"/>
<input type='submit' value='提交' onclick=" startRequest()" />
</form>
<div id="showbox" ></div>
JS部分
var uploadTag;
function startRequest()
{
var json={"randomID":uploadTag};
setTimeout(post_data(obj,'/getuploadprocess',dealResponse),1000);
}
function dealResponse(json)
{
var box=document.getElementById("showbox");
box.innerText=json.progress+'%';
if(json.progress!=100)
{
startRequest();
}
}
//上面这俩个function 组成一个不达目的不罢休的轮询,post_data是个ajax,就不贴了
function setTag()
{
uploadTag=Math.ceil(Math.random()*1000000);
var tag=document.getElementById("uploadTag");
tag.value=uploadTag;
}
//设置随机id,避免搞不清是请求哪个文件的进度
function uploadErr(message)
{
alert(message);
}
//处理错误,当然这里只是简单alert,没有考虑用户体验,就写简单点吧
function uploadEnd(json)
{
var obj=JSON.parse(json)
alert(obj.message)
}
//上传结束的callback,返回图片的获取地址,前端应该将其设置成一个img元素的src属性,也从简了:)
** 后端:**
后端大部分人都是使用的express,然后就顺便设置了app.use(multer(dest:""))解析请求,那可就做不了进度条了,建议把这个设置删了,然后 var formidable=require(“formidable”);(写在路由模块里);
代码:
var uploadprogress={};//全局变量,存进度
module.exports=function(app)
{
...........................................................................
...........................................................................
app.post('/uploadimage',function(req,res){
var id=1;
var uploadfile;
var form=new formidable.IncomingForm();//formidable模块的用法自己去github看文档吧
form.uploadDir='';
from.keepExtensions=true;
form.on("field",function(name,value){//设置唯一可标识的id
id=req.session.user._id+req.body.randomID;//这个value就是表单中文本框的值
});
form.on("err",function(err){
var callback="<script>top.uploadErr('"+err+"');</script>";
res.end(callback);//这段文本发回前端就会被同名的函数执行
})
from.on("progress",function(bytesRecieved,bytesExpected){
uploadprogress.id=Math.ceil((bytesRecieved/bytesExpected)*100);//更新进度
//这里有个id的值是否已经设置的问题,不过没关系,就算仍是最初的1,也只耗一丁点内存
})
form.on("abort",function(){
............
})
form.on("file",function(err,file){
if(err)
{
console.log(err);
var callback="<script>top.uploadErr('"+err+"');</script>";
res.end(callback);
}
uploadfile=file;//记录文件信息
})
form.on("end",function(){
var file=new File()
.......
......
file.save();//将文件信息存入数据库,如果是文章里面的图片的话,就没有必要存了,在前端直接设置src就相当于存了
var fpath=uploadfile.path;
var callback="<script>top.uploadEnd('"+fpath+"');</script>";
res.end(callback);
})
form.parse(req);
})
app.post('/getuploadprocess',function(req,res){
var id=req.session.user._id+req.body.randomID;
var pr;
if(uploadprogress.id)
{
pr=uploadprogress.id;
}
else
{
pr=0;
}
if(pr==100)
{
delete uploadprogress.id;
}
res.status(200).json({"progress":pr});
})
}
ok主要的代码已经贴完了,下面来说一下工作的流程,首先由表单提交文件,在确定文件后会自动生成一个随机ID用于标识,这个ID发给后台和user的id组合为文件的标识;后台接受文件上传请求,formidable开始解析文件,主要监听progress事件,由其返回的参数来更新上传进度。在表单点击提交的时候同时触发startRequest函数,向后台获取上传进度,这个请求由/getuploadprogress处理,里面的代码就是简单的响应和清理功能。自此全部结束,前端获得进度后想要以什么方式展现就随看官发挥吧,我就简单展现其百分比了
PS:如果有大神路过,小弟请教一个问题:这个代码 是实现了功能,但获得进度是个长轮询,不知道对服务器来讲压力大不大。。测试时我同时上传俩个400多M的文件,电脑风扇就转个不停。虽然文件上传成功了,但还是担心性能问题(我用本机做的服务器),求大神留下高见!
@SenYu啊,原来是你啊。 randomID最终和用户ID构成每个上传文件的独一无二的标识。 前端JS中每次执行startRequest都会有1000毫秒延时,ajax->callback这个流程只会执行一次 关于第三点不明白你的意思,干嘛要打到同一个进程里面去,觉得没必要啊
@yyrdl 好忧桑阿里同事竟然不理我。。。
- 我说的是,randomID没错,但是 你代码写错了 .id 改成[id]吧。。。。
- startRequest那个我看错了。但是。。。你确定你的变量作用域都对?obj,json那几个作用域链不大对啊。。。
- 服务器的全局变量global.XXX 是针对 node进程而言的,多进程global对象不同
@SenYu 代码肯定是没错的 .id 和[id]都行,对象是类数组,所以可以通过[id]这种方式获取,javascript权威指南不是写的很清楚么! 说多了没意思,不服气,回学校直接跑给你看,至于风扇不停的转估计是我同时往硬盘快速写大文件造成的,笔记本不能喝服务器比