上传几百MB的视频,或者几个G的备份包,最怕传到一半断网、进程被杀,或者浏览器刷新。之前的进度全部归零,只能从头再来。阿里云OSS的断点续传就是为这种情况设计的。下面从原理讲到代码,再说生产环境里容易踩的坑。

为什么大文件要分片续传
普通的PutObject是一次请求,中间任何环节出错,就得从第一个字节重传。文件小的时候问题不大,文件到了几个G,问题就很明显了:
- 网络一抖就失败,带宽和时间全都白费
- 移动端或弱网环境下,一次传完的成功率明显偏低
- 进度条归零后,用户很快就不相信这次上传能成功
断点续传的做法是把文件切成若干分片,每片单独上传。成功的分片记下来,失败后只重传没完成的那部分。
OSS 里的两种实现方式
在OSS上做断点续传,有两条路。
分片上传(Multipart Upload)
这是底层接口,分三步:InitiateMultipartUpload 初始化任务并拿到UploadId,UploadPart 逐片上传,CompleteMultipartUpload 合并分片。中断之后,只要保存好UploadId,以及每个已完成分片的PartNumber和ETag,就可以用ListParts查询进度,接着上传剩下的分片。
官方SDK的断点续传接口
大部分业务直接用SDK封装好的方法就够了。比如Java SDK的resumableUploadFile,会把进度写进本地的checkpoint文件,进程重启后读取这个文件就能续传。业务代码改动很小,多数后端场景用它即可。
一个在线教育团队的改造
某在线教育团队原来的课件上传是单次POST。单个视频经常在80%左右失败,运营同学只能一遍遍重传。改造时他们做了三件事:
- 前端按每片5MB切分文件,计算每片的MD5,分片信息缓存在IndexedDB里。
- 服务端为每个文件创建OSS分片任务,把UploadId和文件哈希的对应关系存进Redis,过期时间设为24小时。
- 重新上传时,先调用ListParts拿到已完成的分片,跳过它们,只补传缺失的部分。
改造后,弱网环境下一次上传的成功率从约60%升到接近99%。同样一个2GB的文件,平均重传流量下降了七成以上。这套做法的要点是把上传进度当成需要保存的数据来管,代码本身并不复杂。
实现时要注意的几件事
分片大小
OSS对分片有硬性限制:除最后一片外,每片至少100KB;单个文件最多10000个分片;单片最大5GB。实际选择时,常见的取值在1MB到10MB之间。分片切得太小,请求数变多,开销跟着涨;切得太大,一片失败后要重传的数据量也大。5MB到16MB是比较稳妥的起点,之后可以根据实际失败率再调。
并发与重试
分片可以并发上传,但并发数要看带宽和设备能力,一般3到5个比较合适。单片失败时用指数退避重试。要区分可重试和不可重试的错误:网络超时、5xx可以重试;403通常意味着签名失效,重试没有意义,需要重新获取签名。
进度保存
进度可以存在本地文件、数据库或Redis里,关键是和UploadId绑定。如果任务超过OSS设定的保留期,或者UploadId已被Abort,续传就会失败。遇到这种情况,清掉本地记录,重新初始化任务。
未完成分片的清理
用户中途放弃上传时,未完成的分片仍占着存储空间,也会产生费用。可以配置生命周期规则,自动清理长期未完成的分片。如果业务层能判断用户已经放弃,就直接调用AbortMultipartUpload。
签名与安全
前端不要直接持有AccessKey。更稳妥的做法是由服务端通过STS临时凭证或预签名URL授权分片上传,并把可访问的Bucket和对象前缀限制在最小范围内。凭证一旦泄露,影响范围会小很多。
常见问题排查
- NoSuchUpload:多半是UploadId已过期或已被取消,重新初始化任务即可。
- 分片ETag不一致:检查续传时分片内容有没有被改动,确保分片边界和第一次切分时一致。
- 合并失败:确认分片编号连续,每片都已成功上传,并且ETag已记录。
- 进度和实际对不上:以ListParts返回的结果为准,不要只信本地缓存。
写在最后
阿里云断点续传的思路并不复杂:把大文件拆成能单独恢复的小片,同时把进度好好保存。优先使用官方SDK的封装,能省掉很多底层细节。再把凭证授权、并发控制和过期清理处理好,大文件上传的体验会好很多。