直接用 http.Post 上传大文件会失败,因默认客户端无分块逻辑致内存溢出,且服务端限制请求体大小;需用文件哈希+分块序号实现断点续传,服务端通过唯一键查重、加锁写入、校验合并,确保幂等与一致性。

为什么直接用 http.Post 上传大文件会失败
因为默认 HTTP 客户端没有分块逻辑,整个文件读进内存再发,超大文件直接 OOM;服务端也通常限制单次请求体大小(如 Nginx 的 client_max_body_size),上传中途断开就全重来。断点续传的前提是“每次只传一块”,且服务端能识别并合并。
客户端怎么切片并带上唯一标识
关键不是“怎么读文件”,而是“怎么让每块可定位、可重试、不混淆”。推荐用文件内容哈希 + 分块序号做唯一键:
-
fileHash:用sha256.Sum256全量计算一次,作为整个文件 ID -
chunkIndex:从 0 开始的整数,chunkSize固定(如 5MB),最后一块不足也单独发 - HTTP 请求必须带自定义 header:
X-File-Hash: <hash>、X-Chunk-Index: 0、X-Total-Chunks: 12 - 不要用文件名或修改时间当标识——重命名或覆盖后无法续传
服务端如何判断该块是否已存在
收到一块后,先查本地是否存在对应 {fileHash}_{chunkIndex} 的临时文件。存在则跳过写入,直接返回 200 OK;不存在才 os.Create 并 io.Copy 写入。注意两点:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 写入前加文件锁(
flock或基于 Redis 的分布式锁),避免同一文件多块并发写冲突 - 临时文件路径建议统一存到
/tmp/uploads/{fileHash}/,便于后续合并和清理 - 别依赖
Content-Length做校验——代理或压缩可能改它;要用SHA256校验块内容本身
合并完成文件时最常踩的坑
合并不是简单按序 cat 所有 chunk 文件。真实场景中容易漏掉:
立即学习“go语言免费学习笔记(深入)”;
- 没校验所有块是否齐备:需比对
X-Total-Chunks和实际落地 chunk 数,缺块直接 400 - 合并时顺序错乱:必须按
chunkIndex升序读取,不能依赖文件系统遍历顺序 - 最后一步原子性缺失:先写到
final.tmp,os.Rename替换目标文件,否则程序崩溃时留下残缺文件 - 没清理临时目录:合并成功后异步删掉
/tmp/uploads/{fileHash}/,但别在主线程阻塞删
分块上传真正的复杂点不在传输,而在状态一致性——客户端认为“第 5 块已传”,服务端却因网络抖动没收到,或收到两次。所以每块必须幂等,且最终合并前必须强校验完整性和顺序。

















