JS超⼤⽂件上传解决⽅案:分⽚断点上传(⼀)
之前仿造写了⼀个HTML5版的⽂件上传插件,没看过的朋友可以先看⼀下~得到了不少朋友的好评,我⾃⼰也⽤在了项⽬中,不论是⽤户头像上传,还是各种媒体⽂件的上传,以及各种个性的业务需求,都能得到满⾜。⼩⼩开⼼了⼀把。
但⽆论插件再怎么灵活,也难以应付所有的需求,⽐如,你要上传⼀个2G的⽂件。以现在我们的⽹速,恐怕再快也得传半⼩时。要命的是,如果你在上传到90%的时候不⼩⼼关掉了浏览器,或者是⼿⼀抖摁了F5,完了,⼀切还得从头再来。这种⽤户体验简直太糟糕了。所以,断点续传就⼗分有必要了。什么是续传我就不解释了,⽤QQ传⽂件这么多年,⼤家都见过了。
这⾥要说的是断点续传都有哪些技术要点。使⽤传统的表单提交⽂件或是HTML5的FormData都是将⽂件“整块”提交,服务端取到该⽂件后再进⾏转移、重命名等操作,因此,⽆法实时保存⽂件的已上传部分。⽽且在http协议下,我们⽆法保持浏览器与服务端的长连接,不能以⽂件流的形式来提交。所以要解决的问题具体来讲有以下⼏点:
对上传的⽂件进⾏分割,每次只上传⼀⼩⽚。服务端接收到⽂件后追加到原来部分,最后合并成完整的⽂件。
每次上传⽂件⽚前先获取已上传的⽂件⼤⼩,确定本次应切割的位置
每次上传完成后更新已上传⽂件⼤⼩的记录
标识客户端和服务端的⽂件,保证不会把A⽂件的内容追加到B⽂件上
js合并两个数组在参考了张鑫旭⼤哥的后,我将学到的技术应⽤在了我的插件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只能获取到⽂件的原始名称,⽆法正确的与服务器上的⽂件正确匹配。所以真正在项⽬中⽤,还得依靠服务端来保存这些数据。
关于如何将数据存在服务端,已经前端如何取数据,我在下⾯会讲到。
技术要点就上⾯的那么多了,其实也没有多少技术含量哈~来看看我的插件如何使⽤吧。
续传功能的使⽤⽅法
⽂件的引⼊就不讲了,可参考上⼀篇关于插件的介绍。关键点是新增的⼏个配置,先来看⼀下:
在服务端保存数据
⽤户在使⽤上传的时候可能有各种你意想不到的操作,这⾥我发挥想象描述⼀下⽤户可能的⾏为:同⼀台机器使⽤不同帐号登录,上传同⼀个⽂件
⽂件上传了⼀部分,然后修改了⽂件内容,再次上传
⽂件上传完成100%,再次上传该⽂件
同⼀个页⾯有多个上传按钮,上传同⼀个⽂件,或在不同页⾯上传同⼀个⽂件
仅仅上⾯四条,是不是情况就够复杂了?再加上你系统还有⾃⼰的业务逻辑,所以在服务端保存已上传⽂件数据是⾮常有必要的。⽽且保存数据和获取数据的函数都交给你来定义,抱着插件有⾜够的灵活性。
因为涉及到了服务端的技术,⽆法演⽰,我将我项⽬中的真实使⽤场景在此讲解⼀下,来展⽰⼀下如何⾃已定义⽅法来实现服务端保存数据的可靠上传。我定义的getUploadedSize函数如下:
⽂件初始化
⽂件上传完毕的代码
⽂件块的处理代码,up6对⽂件块的处理,以及⽂件续传的逻辑进⾏了⼤幅度的优化,开发者不需要关⼼续传的细节,因为up6默认就是⾃动续传
我向后台的某个地址发送⼀个请求,传递⽂件名和⽂件的最后修改时间为参数,后台根据这两个参数来到与前台所选择的⽂件对应的服务器上的⽂件,将服务器返回的⽂件⼤⼩return出去,来被插件使
⽤。为什么要传递这两个参数呢?我们在前台⽆法知道服务器上的这个⽂件的名称,所以使⽤原始⽂件名作为⼀个辅助标识。为了防⽌⽤户在两次上传间隔修改了⽂件,我们把⽂件的最后修改时间也传给服务端,让服务端进⾏⽐较,若时间不对应则返回已上传⼤⼩为0,重新上传此⽂件。
再来看后台都要做哪些⼯作。数据库中需要有⼀张表来记录每个已⽂件的情况,包含的字段⼤致有:
字段描述
uid⽤户ID
id⽂件ID标识(唯⼀)
lenSvr服务器⽂件⼤⼩
lenLoc本地⽂件⼤⼩
blockOffset⽂件块偏移(在整个⽂件中的位置)
blockSize⽂件块⼤⼩
blockIndex⽂件块索引(基于1)
blockMd5⽂件块MD5
complete当前⽂件是否已经传完
根据client_filename和last_modified_date,再加上系统中的其他关联信息,可以定位到本次上传的⽂件在服务端的⼤⼩,然后返回给客户端。当然这是我⾃⼰的⽤法,你也可以根据⾃⼰的需求灵活设计。总之最终的⽬的就是要到前台选择的⽂件在服务器上真正对应的⽂件,并将已上传⼤⼩正确返回。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论