Skip to content

说说 JS 大文件断点续传

不管什么样的需求当量级达到一定层次的时候都会变得复杂,当上传文件的体积过大时会出现:文件上传失败,失败后又重新上传的问题

断点续传

shell
# 断点续传
断点续传核心是文件分片 + 分片校验:将大文件切割为小分片,上传前校验已完成的分片,仅续传未完成部分;
前端关键步骤:生成文件唯一 hash 切割分片 校验已上传分片 上传未完成分片 通知服务端合并;
依赖服务端配合:需提供分片校验、分片接收、分片合并接口,且服务端需按文件 hash 暂存分片。

# 步骤
文件分片:将大文件按固定大小(如 5MB)切割成多个二进制分片(Blob/File 实例),每个分片标记唯一索引(如 chunkIndex)和文件唯一标识(如 hash)。
文件唯一标识:通过文件内容生成 hash(如 MD5),确保同一文件多次上传的标识一致,服务端可通过该标识关联分片。
校验已上传分片:上传前向服务端请求该文件已上传的分片索引列表,确定需要续传的分片。
分片上传:只上传未完成的分片,每个分片携带 hash chunkIndex,服务端接收后暂存分片。
合并分片:所有分片上传完成后,通知服务端将所有分片按索引顺序合并为完整文件。

拓展

shell
# 切片上传失败后怎么办?切片失败优先重试(指数退避 + 最大次数),状态持久化,服务端校验兜底;
核心思路是重试机制 + 错误隔离 + 状态记录
先尝试进行重新连接,如果连接几次都有问题则停止连接,提示错误,让用户手动重试
将每个切片的状态(未上传 / 上传中 / 成功 / 失败)存储在 localStorage/IndexedDB,避免页面刷新后丢失;

# 上传过程中刷新页面怎么办?本地存储上传状态,刷新后以服务端已上传切片列表为准,仅重传未完成部分;
核心是页面刷新前持久化状态 + 刷新后恢复状态:
状态持久化:实时将「已上传切片列表」「当前上传进度」「文件基本信息(哈希 / 大小 / 名称)」存储在 localStorage/IndexedDB(推荐 IndexedDB,容量更大)
页面加载时,读取本地存储的上传状态
向服务端请求「该文件已上传的切片列表」(双重校验,避免本地状态与服务端不一致)
过滤掉已成功上传的切片,仅重新上传未完成 / 失败的切片;

# 如何进行并行上传?用 Promise 池控制并发数(3-5 个),避免过多请求,任务队列补充执行;
并行上传的核心是控制并发数 + 任务队列 + 进度聚合,避免同时发起过多请求导致浏览器 / 服务端压力过大
限制最大并发数(如 3-5 个,根据业务调整);
将所有切片放入任务队列;
同时执行最多 N 个上传任务,完成一个后从队列取新任务补充;
实现方式:使用「Promise 池」控制并发,避免回调地狱。

# 切片什么时候按数量切?什么时候按大小切?优先按大小切(如 5MB / 片),数量超限则按数量切,平衡请求数和单切片大小;
先按大小切(如 5MB / 片),若切片数量超过阈值(如 50 片),则按数量重新切(如最多 50 片);

# 如何结合 webworker 处理文件上传?主线程处理 UI / 网络,Worker 处理哈希计算 / 切片切割,避免页面卡顿;
WebWorker 用于将耗时操作(如文件哈希计算、切片切割)移出主线程,避免页面卡顿,核心分工:
主线程:负责 UI 交互(进度展示、按钮点击)、网络请求(上传切片)、状态管理;
Worker 线程:负责 CPU 密集型操作(文件哈希计算、切片切割),不接触 DOM / 网络。
核心实现步骤:
创建 Worker 文件(upload.worker.js),处理哈希计算 / 切片切割;
主线程与 Worker 通信,传递文件数据,接收处理结果;
Worker 内避免使用 DOM API,仅处理纯数据逻辑。

# 如何实现秒传?上传前通过文件哈希校验服务端是否已存在,存在则直接返回结果,无需上传。
秒传的核心是上传前校验文件是否已存在于服务端,无需重复上传,
若哈希已存在:直接返回「上传成功」,完成秒传;
若哈希不存在:返回「未存在」,触发正常切片上传;