断点续传其实很简单:把大文件切成小片,给每个传输会话一个唯一 ID,记录每片的偏移与校验,客户端按序或并行上传,服务端保存状态并在最后把片段原子合并。关键在于幂等、校验与状态持久化,配合合理的重试与回退策略,才能既可靠又高效。

先说清楚:断点续传是什么,为什么要做
断点续传(resumable upload/download)指的是在网络中断、客户端崩溃或其他故障后,继续未完成的文件传输,而不需要从头再传一次。想象你在传一个几个 GB 的视频,突然断网——要是得重传一半,那就糟心了。断点续传把文件拆成小片,记录已到位的位置,恢复时只传剩下的片段。
常见场景
- 移动端网络波动:4G/5G 切换、热点断开。
- 桌面客户端上传大文件到云端。
- 分布式备份或同步,需要容忍部分失败并可恢复。
- 对接第三方对象存储(如 S3),需要签名 URL 和分片策略。
核心概念一览(用最简单的语言解释)
- 分片(chunk/part):把大文件分成若干块,每块独立上传或校验。
- 会话 ID(uploadId/sessionId):唯一标识一次上传流程,便于断点恢复。
- 偏移量(offset):指明当前片段在整个文件中的起始位置。
- 校验(checksum/ETag):每片或整文件的完整性校验,比如 MD5、SHA256。
- 幂等性:重复上传同一片不会改变结果或造成错误。
- 原子合并:所有片都到齐后,服务器以原子方式把片段合并成最终文件。
实现要点(分层来看:客户端与服务端)
客户端设计要点
- 分片策略:大小选择一般在 256KB 到 10MB 之间,依据网络情况与并发能力。分片太小会产生大量请求,太大则恢复代价高。
- 并发上行:支持 N 个并行分片上传(典型 3-6),既利用带宽又避免过载。
- 状态持久化:把已上传的分片索引、会话 ID、服务器返回的 ETag 等持久化到本地(文件、数据库或 Key-Value 存储),重启后继续使用。
- 重试与退避:短暂故障采用指数退避重试,区分可重试和不可重试的 HTTP 状态码。
- 整合校验:每片上传后保存服务器校验(如 ETag),最后合并时再校验整文件哈希。
服务端设计要点
- 会话管理:为每次上传创建 uploadId,记录包含文件元信息、期望大小、每片状态(已接收、已合并)等。
- 幂等接口:上传同一分片多次返回相同结果(或明确告诉客户端已存在),避免重复写入。
- 临时存储:未合并前将分片保存在单独路径或对象存储的临时命名空间,定期清理过期会话。
- 并发与锁:合并步骤需要锁或事务,保证不会有两个进程同时合并同一文件。
- 安全与权限:检查上传权限,短期签名 URL 或 Token,避免未授权上传或覆盖。
常见协议与实现方式
实现断点续传的方式有很多,从简单的 HTTP Range 到专用协议。下面列出几种常用方案和优劣。
- HTTP Range(下载):客户端发带 Range 的 GET 请求,服务器返回对应字节范围,适合断点下载。
- 分片上传 + 自定义 API(上传):客户端发起 CreateUpload(获取 uploadId),之后按 part 上传,最后发 CompleteUpload。
- S3 Multipart Upload:成熟方案,支持分片上传、返回每片 ETag,然后 CompleteMultipartUpload。
- TUS 协议:开源的断点续传协议,设计规范、支持扩展,生态良好。
- 浏览器端库(resumable.js 等):简化实现,处理切片、重试、并发等细节。
一步步实现(一个实战流程)
下面用“发视频到云端”当例子,说明一步步要做的事。
第 1 步:开始上传(客户端请求创建会话)
- 客户端请求:POST /uploads,带文件名、总大小、mime type、可选分片大小。
- 服务端响应:{uploadId, expiresAt, suggestedPartSize}
- 客户端保存 uploadId 和分片参数到本地持久化存储。
第 2 步:切片并上传
- 按建议大小切片,给每片编号 partNumber 和 offset。
- 为每片生成校验(如 SHA256)——可在客户端计算,也可由服务端验证。
- 并行上传:PUT /uploads/{uploadId}/parts/{partNumber},附带内容、偏移、校验头。
- 服务端保存分片并返回标识(ETag 或 partETag)。
- 客户端记录每片的已完成状态和服务器返回的 ETag。
第 3 步:校验与合并
- 当所有分片上传完成,客户端调用 POST /uploads/{uploadId}/complete,附带分片清单与每片 ETag。
- 服务端验证每片 ETag、总长度,再以原子方式合并(或者在对象存储中调用多段合并)。
- 合并成功后,服务端返回最终文件的 URL、ETag 和元信息。
第 4 步:失败恢复
- 客户端重启或断网后,可调用 GET /uploads/{uploadId}/status 获取已接收的分片列表。
- 只重传缺失或校验失败的分片。
- 如果会话过期,可重新创建 uploadId 并尽量复用已上传的临时分片(如果服务端支持)。
接口示例(API 设计建议)
| Endpoint | Method | 用途 |
| /uploads | POST | 创建上传会话,返回 uploadId、建议分片大小、过期时间 |
| /uploads/{uploadId}/parts/{partNumber} | PUT | 上传单个分片,需带偏移与校验头 |
| /uploads/{uploadId}/status | GET | 查询已接收分片、状态与元信息 |
| /uploads/{uploadId}/complete | POST | 提交合并请求,提供分片清单 |
| /uploads/{uploadId}/abort | DELETE | 中止并清理会话与临时分片 |
设计细节与陷阱(不要踩的雷)
- 不要只依赖客户端状态:客户端可能丢失本地记录,服务端应支持查询已上传分片。
- 注意幂等性:PUT 同一 part 多次应该是可重试的,返回相同 ETag 或 200/204。
- 锁与并发合并:合并时加分布式锁或用后端对象存储的原子合并 API。
- 分片大小需要权衡:过小请求过多,过大恢复成本高;考虑网络 RTT 和带宽。
- 过期与清理策略:临时分片需要定期清理,避免占满存储。
- 校验粒度:推荐每片校验 + 最终整文件哈希,防止部分错写未发现。
性能与成本优化建议
- 使用 CDN 或就近上传加速入口,减少 RTT。
- 对常见小文件绕过分片流程,直接上传以降低开销。
- 在对象存储支持下使用服务器端合并(如 S3 的 multipart complete),避免下载再拼接。
- 为大文件提供按需压缩与分辨率选项,减少传输体积(视业务场景)。
安全性与权限控制
- 上传请求应携带签名或短期 Token,防止滥用 uploadId。
- 对临时分片路径使用权限隔离,避免未经授权的读取或覆盖。
- 记录审计日志:谁、何时、哪些分片被上传或合并。
测试与校验清单(确保可用性)
- 在断网、客户端重启、并行上传冲突等场景下做压力测试。
- 验证重复上传同一分片的行为是否幂等,是否返回一致 ETag。
- 测试会话过期后的行为:是否能安全清理或恢复。
- 校验最终合并后的完整性哈希与期望值一致。
常见问题快速排查表
| 现象 | 可能原因 | 排查要点 |
| 断点之后无法继续上传 | uploadId 失效或本地丢失会话 | 检查会话有效期,调用 status 接口确认已上传的分片 |
| 合并失败或文件损坏 | 分片缺失或 ETag 不匹配 | 对比分片清单与服务端记录,检查校验值 |
| 服务器负载高 | 过多并发分片写入或保留大量临时分片 | 限制并发上行、定期清理超期会话 |
| 重复写入导致成本上涨 | 幂等性处理不当,导致多次实际写入 | 实现幂等接口,返回已存在提示而不重复保存 |
现实中可借鉴的开源与服务
- TUS 协议与 tusd 服务:标准化的上传协议,适合构建兼容客户端和服务器的生态。
- S3 Multipart Upload:云厂商成熟实现,兼容性好,适合生产环境。
- resumable.js:浏览器端分片上传库,处理切片、重试与并发。
好啦,按上面步骤去做,会让你的断点续传既稳又能应对各种网络“突发情况”。我自己实现过类似方案:把分片大小调到 4MB、并发 4 条、每片保存 SHA256、会话保存 7 天,效果挺稳的——只是需要在细节上多做容错与清理策略,别让临时数据变成长期负担。