浏览器大文件上传的断点续传:分片、校验与幂等合并
上传几百 MB 的视频或数据包时,最糟糕的体验是网络在最后几秒中断,用户只能从头再来。把上传拆成小块,可以缩短单次失败的重试成本;但真正可靠的断点续传还需要服务端状态、完整性校验和可重复执行的接口。
先把上传设计成一个会话
客户端先请求创建上传会话,服务端返回 uploadId、建议分片大小和过期时间。假设分片大小为 4 MiB,分片总数就是文件大小向上取整后除以分片大小。客户端按序号上传每一块,并携带文件总大小、分片序号和校验值。不要仅用文件名作为会话标识,因为同名文件很常见;文件大小和修改时间可以帮助恢复本地状态,却不能证明文件内容相同。
分片大小需要根据网络和服务端限制调整。分片太小会产生大量请求和元数据,太大则让失败重传变得昂贵。浏览器可用 Blob.slice() 读取分片,并将哈希计算放到 Web Worker,避免大文件校验占住主线程。
恢复时以服务端记录为准
页面刷新或重新打开后,客户端先查询 uploadId 的会话状态,服务端返回已经确认的分片序号。客户端只补传缺失部分,不要把本地“已发送”当作“已保存”:请求可能已到达服务端,但响应在网络中丢失。每个分片都带校验值,服务端保存前验证内容;上传全部完成后,再校验总大小和整文件摘要。这样既能发现传输损坏,也能避免把不同文件的分片拼在一起。
如果浏览器本地缓存了 uploadId,还要先确认所选文件与原会话匹配。无法可靠确认时,提示用户重新选择文件,而不是静默续传到错误对象。
让重试安全且有边界
分片接口应具备幂等性。同一 uploadId 和序号收到相同校验值时,可返回已成功;如果相同序号对应不同内容,应返回冲突并要求重新创建会话。这样客户端在超时后重发请求,不会生成重复分片。完成合并的接口也应幂等:重复提交时返回同一个已完成结果。
并发数控制在少量范围,例如 3 到 4 个请求,再根据终端和网络调整。只对超时、网络中断、限流或服务端暂时错误重试,并采用带随机抖动的退避;参数错误、权限错误等明确的客户端错误应直接展示原因。重试次数要有限,收到 Retry-After 时遵守服务端给出的等待时间。进度条以服务端确认的字节数为准,避免把尚未保存的请求算成完成。
合并之前再做一次完整检查
客户端提交完成请求后,服务端检查分片是否齐全、序号是否连续、每块长度和校验值是否符合预期。随后先合并到临时对象,校验整文件大小与摘要,通过后再原子地标记为可用。不要根据用户提供的文件名直接拼接磁盘路径;对象名应由服务端生成,并验证账号对上传会话的所有权。
会话还需要清理策略。用户取消上传时可保留会话供稍后恢复;长期未完成的会话则应在过期后删除分片和元数据,并释放配额。清理操作也要可重复执行,以免定时任务中断后留下孤儿文件。
用故障场景验证流程
上线前至少验证:最后一片小于分片大小、上传中途断网、服务端已保存但响应丢失、重复提交同一分片、替换成同名不同内容的文件、会话过期,以及合并校验失败。断点续传的核心不是多次发送请求,而是每一步都有可查询的状态、明确的校验规则和安全的重试边界。
- 点赞
- 收藏
- 关注作者
评论(0)