持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
在前端,用户体验是非常重要的,而它又是很有广度的一个话题,会涉及到比较多的知识点,但是掌握之后,对自己会有不错的提升。今天来看看大文件上传是如何做体验优化的。
大文件分片上传
本文将从大文件分片上传切入,先会交代JS处理原始文件数据的方式,然后到前端二进制,接着是文件的分片,以及分片上传的优势,最后是如何把文件上传做到极致。主要有以下几点:
- JS处理原始文件数据的方式 (File、Blob、FileReader、ArrayBuffer、base64)
- 前端二进制 (ArrayBuffer、TypedArray、DataView、Blob、File、Base64)
- 文件的分片、秒传、断点续传 (实现分片的基石)
- 分片上传的优势 (更好的用户体验)
- 把文件上传做到极致 (利用时间分片
Time Slicing)
JS处理原始文件数据的方式以及前端二进制
File:页面上使用 <input type="file">作为接收文件的媒介,选择文件之后,浏览器就能拿到一个File对象:
每个
File 对象都包含文件的一些属性,这些属性都继承自 Blob 对象:
lastModified:引用文件最后修改日期,为自1970年1月1日0:00以来的毫秒数;lastModifiedDate:引用文件的最后修改日期;name:引用文件的文件名;size:引用文件的文件大小;type:文件的媒体类型(MIME);webkitRelativePath:文件的路径或 URL。
而 File对象是特殊类型的 Blob,Blob 的属性和方法都可以用于 File对象。
就是说,File对象具有 .slice 方法,这就是文件实现分片的基石,但是这里先按下不表,后续会有介绍。
ArrayBuffer:ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。
它是一个字节数组,通常在其他语言中称为“byte array”。
你不能直接操作 ArrayBuffer 的内容,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。
接下来我们看看这几个前端二进制之间的关系:
图1(图片来源于网络)
文件的分片、秒传、断点续传
大文件快速上传的方案,相信你也有过了解,其实无非就是将 文件变小,也就是通过 压缩文件资源 或者 文件资源分块 后再上传。
上面我们拿到了File对象,然后使用.slice 方法,就可以将一大坨的二进制分成我们需要的小块,具体一块要多大就要视情况而定了,这里没有最好的分片体积,只有最合适的分片体积。
将这些分片都分配唯一Key,前端发送一个,服务端就可以返回一个唯一Key和成功标识,前端就能知道哪些分片已经发送成功了,这个过程有点像TCP的滑动窗口原理,也就是说在我们项目里实现了一个应用层面的TCP滑动窗口 ( *ˇωˇ* )
然后我们将分片的文件丢进去异步请求中,给服务端发送数据(这里先不考虑请求失败和分片数据校验),可以用promise.all实现,当所有的分片都请求成功时,就说明服务端已经拿到了整个分片内容,他们重新拼接就可以得到文件了。
分片上传的优势
上面我们做了那么多,有人可能会问:究竟大文件分片上传有什么优势呢,用来显示上传进度?
答:上传进度直接用progress就好啦,分片上传的优势在于可以获得上传主动权,就是说上传时机可控,上传并发可控,上传出错的回调也可以自定义。我们一个一个来说。
上传并发可控:我们将文件分片了,接下来的任务是把这些分片一个一个上传上去,而我们知道,浏览器一般会限制同一时间同一域名下的请求个数,但是只要不超出这个个数,理论上我们是可以将多个文件分片同时上传去服务端,进而提高传输效率(理论上),而在网络不好或者页面中有更高优先级的请求要处理的时候,又可以优先处理其他高优先级的请求了。
把文件上传做到极致
在分片之后,可以对每一段分片进行加密和加上文件校验。加密又是一个比较广泛的话题,以后有空再补上。
文件校验:服务端想要知道这个分片是不是完整的,就需要前端用MD5或SHA-256生成的哈希值,在请求的时候一并传输给服务端,服务端接收到文件后,利用相同的加密原理,如果得到一样的哈希值,说明这段文件分片内容是完整的。
但是,前端对每一段分片进行MD5或SHA-256处理,在前端那么注重时间复杂度的环境下,任何操作都可能需要考虑优化的,何况体量可能是1G,2G的大文件的分片后的处理,是一个性能优化的突破口。
这时可以参考React在diff做的时间分片(Time Slicing),利用每一帧去,对一小片文件分片进行处理,这样用户可能就不会感知到页面卡顿了。
小结
大文件上传没有那么高深莫测,都是基础的叠加,像一些前端二进制、实现类似TCP滑动窗口功能、promise、数据加密、校验等,这些都去了解了之后,回来用在大文件上传,就会觉得得心应手了。
最后,让我们一起加油吧!