参加齐鲁软件设计打扫的时候,在用户上传文件那块遇到了很大的难度,因为用户需要上传视频等大文件,所以html自身的file标签就不能再使用了,于是在网上看了很多办法,最后选择了一个分片上传的插件plupload,那为什么要使用切片上传?什么是切片上传呢?
为什么要使用切片上传:
1、后台脚本的运行时间是有限制的,超过脚本的运行时间但文件未上传完成会造成文件上传失败。
2、后台接收文件的时候是要消耗服务器的内存的,长时间传输同一个文件会消耗掉服务器大量的内存,严重的甚至造成服务器崩溃。
切片上传的概念及基本原理:
切片上传是从htm5开始兴起的,他可以利用js的slice(star,end);方法对获取的文件进行数据截取,就是可以获取到文件某一部分的数据,有了这个东西之后,只要定义好每一片的文件大小,就可以将这一个大文件进行切片,每次只传输一片文件,这样就可以分片对文件进行传输,当然,有切片就要有组合,这时候普通的后台接收文件的方法就不适用了,具体的接收原理下面再补充。
主要JS代码:
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
var chunck_upfile = function(opt){ //定义类chunk_upfile this.block=opt.chunk*1024*1024; //每一片文件的的大小 var _this = this; //定义对象自己,用于下面的属性调用 this.if_allow=false; //定义是否允许上传文件 $(opt.file_id)[0].onchange = function(){ //当选择文件之后触发 chunck_upfile.prototype.file= $(opt.file_id)[0].files[0]; if(opt.fileChange instanceof Function && _this.judge()){ opt.fileChange(_this.file); } } this.judge=function(){ //判断是否符合上传规则 if(opt.allow_type.indexOf(_this.getsub())>-1){ //查找文件的扩展名是否在允许范围内 _this.if_allow=true; }else{ _this.if_allow=false; alert('不允许上传的类型'); return false; } if(_this.file.size>opt.max_size*1204*1204){ _this.if_allow=false; alert('文件大小超出限制'); return false; } return true; } $(opt.sub_id)[0].onclick = function(){ //点击提交之后触发 var file_name_len=$(opt.file_id).val(); if(file_name_len.length<1){ alert('未选择文件'); return false; } _this.judge(); if(_this.if_allow) _this.init(); } this.init=function(){ //初始化获取文件信息 if(!this.file){ return false; } if(opt.rand_name){ //判断是否需随机文件名 //定义新的文件名 chunck_upfile.prototype.new_file_name=new Date().getTime()+Math.ceil(Math.random()*1000)+"."+this.getsub(); } chunck_upfile.prototype.all=this.file.size; //定义文件的总大小 //定义文件的切片数量 chunck_upfile.prototype.num = this.all/this.block==parseInt(this.all/this.block)?this.all/this.block : Math.ceil(this.all/this.block); _this.sub(1); //开始提交 } this.getsub=function(){ return _this.file.name.split('.').pop().toLowerCase(); } this.sub=function(i){ var star = (i-1)*this.block; if(this.all<this.block){ //判断文件的总大小是否比最小的那一片小 var end=this.all; //截取的数据到文件尾部 }else{ var end=this.block+star; } if(end>this.all) end=this.all; //判断最后截取的数据是否超出文件的大小 // console.log(star+"---"+end); dat=this.file.slice(star,end); //开始截取文件 var fd =new FormData(); if(opt.rand_name){ //将文件的name添加进FormData(),并以堆积文件名传输 fd.append($(opt.file_id).attr("name"),dat,this.new_file_name); }else{ //将文件的name添加进FormData(),并以原文件名传输 fd.append($(opt.file_id).attr("name"),dat,this.file.name); } //向后台传送当前是第几片和总的片数,用于最总的文件更名 fd.append('chunk',i); //当前的分片数 fd.append('chunks',this.num); // 总共的片数 var xh = new XMLHttpRequest(); xh.upload.onprogress = function(evt){ //上传过程中触发 var loaded = evt.loaded; //每一片中已上传的数据大小 var tot = evt.total; //每一片中文件的总大小 var per = Math.floor((100 * loaded / tot)*(i/_this.num)); //已经上传的百分比 if(opt.uploading instanceof Function){ //判断uploading是否是可执行函数 opt.uploading(per); } } xh.onreadystatechange = function() { //上传完成之后执行 if (xh.readyState == 4 && xh.status == 200 && i==_this.num) { //需要返回状态4才是完成后 if(opt.callback instanceof Function){ //判断callback是否是可执行函数 opt.callback(xh.responseText); } }else{ if( xh.readyState == 4 && i<_this.num){ _this.sub(++i); } } } xh.open("post",opt.url); //同步post请求!!很重要 xh.send(fd); } } |
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var star=new chunck_upfile({ //新建对象 file_id:"#f2", //file文件的id sub_id: "#sss", //提交按钮的id chunk:20, //每一片文件的大小M max_size:20, //文件最大限制M url: 'up.php', //后台接收的路径 rand_name:true, //是否自动更改名称 allow_type:['docx','zip','doc','jpg'], //允许上传的文件类型 fileChange:function(obj){ //选择文件之后触发 }, callback:function(res){ //上传完成后触发,返回后台的数据 //console.log(res); }, uploading:function(per){ //上传过程中触发,返回上传进度,可用于写进度条 console.log(per); }, } ); |
程序实现说明:
程序基于参数对将要上传的文件进行配置,在JavaScript中新建FormData()对象,将获取到的input文件信息填充到FormData中,然后基于JavaScript的XMLHttpRequest();对文件进行静态的上传。可能有人会问,上面既然说了使用了JQ框架,为什么还要使用XHR却不使用$.ajax(),在这里我说明一下,其实我刚开始写的时候是用了JQ的ajax,但是后来我有使用了JS原生的ajax,在本程序中,JQ只起到获取元素和获取元素属性的作用,确实未使用其ajax功能,原因就是JQ的ajax不能实现onprocess的功能,即不能获取文件上传的实时情况,也就意味着无法显示文件的上传进度,所以还是不用框架的ajax了吧。