之前仿造uploadify写了一个HTML5版的文件上传插件,没看过的朋友可以点此先看一下~得到了不少朋友的好评,我自己也用在了项目中,不论是用户头像上传,还是各种媒体文件的上传,以及各种个性的业务需求,都能得到满足。小小开心了一把。
但无论插件再怎么灵活,也难以应付所有的需求,比如,你要上传一个2G的文件。以现在我们的网速,恐怕再快也得传半小时。要命的是,如果你在上传到90%的时候不小心关掉了浏览器,或者是手一抖摁了F5,完了,一切还得从头再来。这种用户体验简直太糟糕了。所以,断点续传就十分有必要了。什么是续传我就不解释了,用QQ传文件这么多年,大家都见过了。
这里要说的是断点续传都有哪些技术要点。使用传统的表单提交文件或是HTML5的FormData都是将文件“整块”提交,服务端取到该文件后再进行转移、重命名等操作,因此,无法实时保存文件的已上传部分。而且在http协议下,我们无法保持浏览器与服务端的长连接,不能以文件流的形式来提交。所以要解决的问题具体来讲有以下几点:
对上传的文件进行分割,每次只上传一小片。服务端接收到文件后追加到原来部分,最后合并成完整的文件。
每次上传文件片前先获取已上传的文件大小,确定本次应切割的位置
每次上传完成后更新已上传文件大小的记录
标识客户端和服务端的文件,保证不会把A文件的内容追加到B文件上
在参考了张鑫旭大哥的这篇文章后,我将学到的技术应用在了我的插件Huploadify中,成功的添加了断点续传功能。在此将技术和插件都分享给大家。
工作原理/技术要点首先的首先,要明确,如果我们有一个10M的文件,每次切割上传1M,那么是需要发10次请求来完成的。在http协议下,只能这么搞。断点上传分三步来完成:
选择一个文件后,获取该文件在服务器上的大小,通过本地存储或自定义的函数来获取。
根据已上传大小切割文件,发出n次请求不断向服务器提交文件片,服务端不断追加文件内容
当已上传文件大小达到文件总大小时,上传结束
首先是文件的分割,HTML5新增了Blob数据类型,并且提供了一个可以分割数据的方法:slice(),其用法和字符串、数组的slice()方法一样,可以截取一个二进制文件的一部分。
其次是文件片的保存与追加,我后台用PHP写的,先用file_get_contents获取文件的二进制格式,再用file_put_contents每次将文件追加,具体的写法可以参照后面,或者是下载我打包好的文件。
接下来我们还需要实时保存已上传文件的大小,以便于下次上传前进行正确切割。使用HTML5的localStorage是一种方法,将已上传的大小保存在本地,下次上传前先从本地读取。不过这种方式是很局限的,抛开用户可能通过各种管家清除掉本地数据不讲,假如用户在A页面上传了一个文件的50%,然后在B页面想把该文件上传到另外一个地方,结果从本地一读文件已上传50%了,直接从51%的位置开始上传了,显然是个错误。问题就在于本地不能存太多的信息,通过File API只能获取到文件的原始名称,无法正确的与服务器上的文件正确匹配。所以真正在项目中用,还得依靠服务端来保存这些数据。
关于如何将数据存在服务端,已经前端如何取数据,我在下面会讲到。
技术要点就上面的那么多了,其实也没有多少技术含量哈~来看看我的插件如何使用吧。
续传功能的使用方法文件的引入就不讲了,可参考上一篇关于插件的介绍。关键点是新增的几个配置,先来看一下:
breakPoints:false,//是否开启断点续传 fileSplitSize:1024*1024,//断点续传的文件块大小,单位Byte,默认1M getUploadedSize:null,//类型:function,自定义获取已上传文件的大小函数,用于开启断点续传模式,可传入一个参数file,即当前上传的文件对象,需返回number类型 saveUploadedSize:null,//类型:function,自定义保存已上传文件的大小函数,用于开启断点续传模式,可传入两个参数:file:当前上传的文件对象,value:已上传文件的大小,单位Byte saveInfoLocal:false,//用于开启断点续传模式,是否使用localStorage存储已上传文件大小