业余将大文件上传重新梳理了一遍,后端基于Nodejs;有几个要点感觉很好玩:
- 兼容性:IE789为代表;
- 跨域上传:document.domainmiddlePage;
- 多文件上传:input['type=file'] multiple;
- 拖拽上传:drag drop;
- 大文件分段:files.slice(s,e);
- 断点续传:localStorage;
- 接收分段的文件:formidable.onPart;
- 陆续写入分段文件:fs.write(fd,bf,offset,length,position,cb);
很明显,重点都跟老旧IE没关系,在此也不打算用flash补充了,直接用最原始的做法:input['type=file']+hideFrame;所以,任务重心也就是HTML5了,坑并不多,因为所有知识点百度都能得到;可是事实告诉我,实际是与理论有差距的,特别是一些知识点还要缠在一起的时候,可能百度只是做到了授人以鱼,而要学会渔,还得自个儿折腾;
接着:
一步一步到现在。。。
1、对于低版本的IE789
说到要兼容IE78,顿时感觉自个儿这个小前端太苦逼,仅仅为了两个微软不要的浏览器,还要专门弄一堆乱七八糟hack,尼玛,有时为了兼容的代码,比原本的代码还多好多,有木有。。。好吧,原谅我,今天的天气确实挺燥热的;国情使然嘛,特殊项目还是得特殊对待,所以对于IE低版本,只求能上传,基本样子在那就行了;別犟了!
2、跨域上传
如果上传的程序是独立的站点或在静态资源站,那就要处理跨域问题了;一般较多的做法就是document.domain;另外还有一种采用middlePage的做法,我更喜欢后者;如果是要跨域的话,就得使用iframe引用那个上传文件;只需要在src上带上middlePage的整个路径,同时middlePage位于调用站而且也是一个影藏的iframe,文件上传完毕后把datas作为参数重新赋值到middlePage这个iframe上,然后在middlePage里调用callback;即可解决跨域问题;
例如在3000端口的站点调用端口为5000的站点里的上传,iframe的src:
http://localhost:5000/uploader?setDomainPath=http://localhost:5000/middlePage.html
middlePage里的回调:
window.parent.parent.callback(JSON.parse(utils.url.query.datas));
然后调用站的callback即可拿到datas;
3、多文件上传
多文件上传只需在input['type=file']加上multiple属性(同样是HTML5里新增的)即可,好吧,其实大家都知道的;重点在与拖拽上传的纠葛上;
4、拖拽上传
好吧,拖拽上传也就那样,dragenter、dragover、dragleave、drop就over了;接着上面:重点在于与多文件上传的纠葛上;一是multiple的多文件上传是input的change事件触发;另外拖拽的上传由drop触发;多个文件我是一个上传完毕后紧接着下一个开始;OK,问题来了,我先用change上传几个文件,然后再拖一批文件上传,还有单个文件可以取消或者删除,多次反复拖拽后,是不是顿时感觉绕到一起了;所以问题是否简单,不在于问题本身,而在于问题涉及到的其他问题以及你看待问题的角度和处理问题的能力;
因为多文件上传需要创建多条显示信息,每条上面绑两个值,一个是data-index,每个待上传文件的索引值,一个status,默认为0,上传成功后改为1;files本身为一个类数组的对象,它的索引可以与data-index对应,从data-index=0开始上传,完毕后寻找第一个status=0的item所对应的data-index是否存在,存在就上传这个,不存在即全部上传完了;但是onchange和drop的上传反复合到一起后,问题就来了,维护一个files远远不够;也就是,多种情况的files得合并,加上之前说的从哪开始上传由status=0对应的data-index决定的,所以合并files,也就是将files这个类数组对象转换为数组,然后多次操作合并他们,这样,就只需要维护这个合并后的数组即可;至于可能会删除某条,只需删掉其DOM,是否删掉文件,暂未处理,同时要保证这个合并后的数组的完整性,因为删掉之后可能还会再拖一批上传,保证data-index与合并后的数组的索引一一对应;这样拖拽与multiple的纠葛就化解了;
var filesArr=null;
var startFile=function(files) {
var ff=$('.prograssInfo[status=0]').first();
var ins=ff.attr('data-index');
if (ins&&window.dropedCouldStart) {
ff.find('.delthis').hide();
var Files=new utils.Files({
files:files[ins],
chunkSize:10*1024*1024,
fieldIndex:ins,
onprogress:function(p){
window.dropedCouldStart=false;
callbk(p,ins);
if(p>=100){
window.dropedCouldStart=true;
$('.prograssInfo[data-index='+ins+']')
.attr('status',1).find('.delthis').show();
startFile(filesArr);
//确保files对象为最新合并后的
}
}
});
Files.postFiles();
}
}
var dropCount=0;
var createField=function (files) {
if (files) {
var _filesArr=[].slice.call(files,0);
var _files=[];//过滤type size
for(var i=0;i<_filesArr.length;i++){
var _name=_filesArr[i].name;
if(_filesArr[i].size>maxFileSize
$.inArray(_name.substr(_name.lastIndexOf('.')+1).toLowerCase(),accept)!=-1){
_files.push(_filesArr[i]);
};
}
if (filesArr&&filesArr.length) {
filesArr=filesArr.concat(_files);
}else{
filesArr=_files;
}
for (var i = filesArr.length-_files.length; i < filesArr.length; i++) {
$(contID)
.append(fieldItem(i,filesArr[i].name,utils.format.fileSize(filesArr[i].size)));
//data-index对应i的值,确保删除一条后index还能一一对应
}
if (window.dropedCouldStart==true&&filesArr&&filesArr.length) {
$('#uploadBtn').hide();
startFile(filesArr);
}
}
}
5、分段与断点续传
参考之前的:基于Nodejs的大文件上传之断点续传
6、分段接收与陆续写入
参考之前的:扒一扒Nodejs formidable的onPart
7、后端是Nodejs,就会那么一点,不献丑了;关于文件上传,网上成型的也是一大把,作为我前端路上必备的一大组件,我想自己实践一把,肯定感觉不一样,因为不管自己实现的还是网上的代码量都并不少,这其中的细节处理以及兼容问题,还有对HTML5的应用,对我都是挺有挑战意义的,当然,重点还是基础的积累,别人写的,拿过来用的是挺简单的,但是就前端而言,对于技术点的应用,你并不太理解,对应后端的处理,更是很难知道人家做了什么;所以,这样慢慢的肯定限制了你对前端技术点的扩展;
8、实测地址:http://images.famanoder.com/uploader?show=1
苦逼前端弄的破服务器,大家别往死里测啊!周末再梳理下,就会关闭这个地址的;
事实证明挺有搞头的,加油!
原文来自:花满楼http://www.famanoder.com/bokes
热门评论
页面里面没数据
5、分段与断点续传
参考之前的:基于Nodejs的大文件上传之断点续传
6、分段接收与陆续写入
参考之前的:扒一扒Nodejs formidable的onPart
7、后端是Nodejs,就会那么一点,不献丑了;关于文件