大文件上传:分片、断点续传与秒传的三件套
上传一个 10MB 的文件和上传一个 2GB 的视频,完全是两回事。前者一次 POST 就能解决,后者一旦网络抖动就得从头再来,用户等得起,产品等不起。解决办法不是把超时时间调大,而是把上传拆开:分片、断点续传、秒传,这三件套组合起来,才是一个能用的大文件上传方案。
分片:先把文件切开
分片是基础。浏览器端用 File.slice 把文件切成固定大小的块,比如每块 5MB,一个 2GB 的文件就变成 400 多个分片。每个分片独立上传,互不依赖。
分片带来三个直接好处。第一,单个请求变小了,超时和失败的概率大幅下降;第二,分片可以并发上传,带宽利用率上去,总耗时下来了;第三,某个分片失败了,只重传那一个分片,不用重传整个文件。
分片大小要根据场景调。块太小,请求数爆炸,服务端合并的开销也大;块太大,单片失败的代价又高。一般 2MB 到 10MB 是个合理的区间,弱网环境可以再切小一点。
断点续传:失败了从断处接上
有了分片,断点续传就顺理成章。上传前先问服务端:这个文件已经传了哪些分片?服务端按文件标识返回已收到的分片序号,浏览器跳过这些分片,只传剩下的。
这里的关键是文件标识。不能用文件名,两个同名文件内容可能完全不同。常用做法是算整个文件的哈希,或者用文件名加大小加修改时间拼一个标识。哈希最准确,但大文件算哈希本身就耗时,可以只抽样算一部分,或者用 Web Worker 在后台算,不阻塞页面。
服务端收到分片后先落盘,等所有分片到齐再按序号合并成完整文件。合并完要校验一次完整文件的哈希,对不上就说明有分片损坏,得让客户端重传对应分片。
秒传:别人传过的,我就不用传了
秒传是断点续传的极端情况:上传前先把文件哈希发给服务端,服务端一查,这个文件已经有人传过了,直接返回成功,不用再传一个字节。
秒传省的是带宽和时间,但要注意两个问题。第一,隐私与安全:只凭哈希就认定文件相同,理论上存在哈希碰撞,虽然概率极低,敏感场景还是要做二次校验。第二,秒传不能绕过权限检查,文件归属和访问控制必须在秒传判断之前就做完,否则就成了越权读文件的口子。
服务端的三件事
客户端做得再好,服务端跟不上也是白搭。服务端至少要做好三件事。
第一,分片存储要支持乱序到达。分片是并发上传的,到达顺序不确定,服务端按序号落盘,不能假设按顺序来。
第二,未完成的分片要定期清理。用户传了一半就关掉页面,剩下的分片就成了垃圾。给上传会话设一个过期时间,到期没合并就清理,避免磁盘被半成品占满。
第三,合并是重操作,要异步做。收到最后一个分片后先回成功,再在后台合并校验,不要让合并阻塞上传响应。
一个容易踩的坑:并发数
分片并发不是越多越好。浏览器对同一域名有并发连接数限制,一般是 6 个,开 20 个并发也不会真的同时跑 20 个。更重要的是,并发过高会把服务端的磁盘 IO 和连接池打满。实践中 3 到 6 个并发分片是比较稳妥的选择,再配合失败重试和退避,效果比一味加并发好得多。
小结
大文件上传没有银弹,是三件套的组合:分片把大问题切小,断点续传让失败可以接续,秒传把重复劳动直接省掉。服务端再把乱序存储、过期清理、异步合并这三件事做好,一个 2GB 的文件上传,也就和传 10MB 的文件一样稳了。
- 点赞
- 收藏
- 关注作者
评论(0)