HTML5实现多文件上传(支持文件拖拽及上传进度与速度)

本文介绍通过HTML5新增的多个API实现多文件(拖拽)上传,支持上传进度及上传速度显示。

本文示例可以在https://www.lyz810.com/demo/fileUpload/上查看。

一、input的multiple属性
input的file类型中新增了multiple属性,用于选择多个文件:
<input type="file" multiple="multiple" />

二、input的accept属性
accept属性用于限制选择文件的类型,例如accept=”image/*”则只能选择图片,其值为文件的MIME类型,可以由逗号分割多个值,如accept=”image/*,text/plain”。MIME类型可以参考http://www.w3school.com.cn/media/media_mimeref.asp

注意:此处只是限制打开文件选择框时默认展现的文件类型,用户可以通过更改选择文件类型,选择其他类型的文件,所以应该对文件类型进行校验。

三、progress元素
progress元素为HTML5新增元素,用于显示进度条,默认样式如下: 你的浏览器不支持此标签

元素的max属性为进度条满时的值
给元素设置value属性,即可控制进度条的进度,如设置进度为50%(value=50,max=100): 你的浏览器不支持此标签

四、拖拽事件
本例中使用的拖拽事件有以下几个:
1.ondragover:当文件出于元素上方时触发
2.ondragout:当文件脱离元素时触发
3.ondrop:当文件在元素上,并且鼠标释放时触发

五、通过Ajax上传文件
本例中,多个文件是通过多个请求上传的。这样上传的好处是,对于每一个文件来说,都可以根据上传结果来判断是否需要重传。
也可以通过一个请求同时上传多个文件,但如果上传过程中出错,那么重传的成本要更高,具体使用哪种方式可以根据自己的实际需要进行选择。
本例中,若服务器返回5xx状态码则认为上传失败,会重新上传失败的文件(如果失败3次则不再尝试重传)。
代码片段如下(截取核心代码,完整代码请进入Demo页面查看):

function uploadFile(index){
  var formData = new FormData();
    formData.append('file', files[index]);
    var xhr = new XMLHttpRequest();
    xhr.open('POST', './backend.php', true);
    xhr.onreadystatechange = function(){
      if(xhr.readyState === xhr.DONE){
        if(xhr.status >= 200 && xhr.status < 300){ 
          failCount = 0;
          if(files[++index]){ 
            uploadFile(index);
          } 
        } else if(xhr.status >= 500 && xhr.status < 600){ 
           failCount++;
           if(failCount > 3){
             failCount = 0;
             if(files[++index]){
               uploadFile(index);
             }
           }else{
            setTimeout(function(){
              uploadFile(index);
            }, 1000);
          }
        }
      }
    };
    xhr.send(formData);
}

六、上传进度与上传速率查看
XMLHttpRequest对象的upload.onprogress事件可以提供上传进度相关的数据
它的参数中包含了已上传字节数loaded、总字节数total、时间戳timeStamp
通过上传字节数/总字节数可以得出上传进度的百分比
通过两次onprogress事件timeStamp之差可以得出两次的时间差(毫秒),通过两次的loaded值可以得出该时间段内上传的字节数
多个文件上传时,可以算出一个文件占整体的百分比,当前上传的文件索引可以得出已经上传的文件个数,用已上传的文件个数乘上每个文件的占比,就可以得到当前文件上传起始时的整体百分比,加上当前文件上传的百分比*每个文件占用的百分比即可得出整体进度的百分比。
这样就可以大致的算出上传的速率:

var timeStamp = 0,
  loaded = 0,
  start = Date.now();
xhr.upload.onprogress = function(e){
  var curProgress = (e.loaded * 100 / e.total).toFixed(2),
    //index:当前文件索引,files:所有要上传的文件
    totalProgress = index / files.length + curProgress / files.length * .01,
    timeInterval = 0,
    rate = 0;
  if(timeStamp > 0){
    timeInterval = e.timeStamp - timeStamp;
  }else{
    timeInterval = Date.now() - start;
  }
  rate = (e.loaded - loaded) / 1024 / timeInterval * 1000;
  timeStamp = e.timeStamp;
  loaded = e.loaded;
};

Fetch API用法示例

本文介绍fetch函数的几个常用用法。

本文中所有示例均可在https://www.lyz810.com/demo/fetch/中查看演示

实例一、最简单的发送GET请求,返回文本数据
fetch会返回一个promise,可以使用then的第一个函数接收响应对象(Response对象)

function func1(){
  fetch('./api.php?action=getPlain')
   .then(function (response){
    //text方法将响应转为纯文本
    return response.text();
   })
   .then(function (text){
    alert(text);
   });
 }

实例二、发送GET请求,带自定义头,返回json数据
fetch的第二个参数为一个Request对象,headers为请求头的参数,例子中添加了一个自定义的请求头userHeader,值为myHeader

function func2(){
  fetch('./api.php?action=getJson',{
   headers:{
    userHeader:'myHeader'
   }
  })
   .then(function (response){
    //json方法将响应转为json对象
    return response.json();
   })
   .then(function (json){
    alert(JSON.stringify(json));
   });
 }

实例三、发送POST请求,通过Request类及Header类,返回json数据
可以直接通过Request类和Header作为fetch的参数

function func3(){
  var headers = new Headers();
  headers.set('userHeader', 'myHeader');
  headers.append('Content-type','application/x-www-form-urlencoded');
  var request = new Request('./api.php?action=getJson',{
   headers: headers,
   method: 'POST'
  });
  fetch(request)
   .then(function (response){
    return response.json();
   })
   .then(function (json){
    alert(JSON.stringify(json));
   });
 }

实例四、发送HEAD请求,只获取响应头的信息
发送HEAD请求,通过response对象的headers属性的get方法可以获取响应头信息

function func4(){
  fetch('./api.php?action=getPlain',{
   method: 'HEAD'
  })
   .then(function (response){
    alert(response.headers.get('Response-key'))
   });
 }

实例五、带cookie发送POST请求,包含请求体
默认情况下,fetch不会带着cookie,必须使用credentials: ‘include’参数才可以使请求带上cookie

function func5(){
  fetch('./api.php?action=getJson', {
   method: 'POST',
   credentials: 'include',
   headers:{
    'Content-Type': 'application/x-www-form-urlencoded'
   },
   body: 'test=test'
  })
   .then(function (response){
    return response.json();
   })
   .then(function (data){
    alert(JSON.stringify(data));
   });
 }

实例六、POST请求,body内容为formData
body的内容不仅支持字符串还支持formData、Blob等类型

function func6(){
  //这种方式可以用来提交表单,上传文件等
  var formData = new FormData();
  formData.set('key', 'value');
  fetch('./api.php?action=getJson', {
   method: 'POST',
   headers:{
    'userHeader': 'This is my header!'
   },
   credentials: 'include',
   body: formData
  })
   .then(function (response){
    return response.json();
   })
   .then(function (data){
    alert(JSON.stringify(data));
   });
 }