分片上传需客户端按固定大小切片并携带元信息,服务端逐片落盘校验后原子合并。关键在流式读写、路径命名、长度校验、临时文件与状态一致性保障。

分片上传的核心逻辑是客户端切片 + 服务端合并
Go 本身不提供「自动分片上传」的内置机制,关键在于自己控制文件读取边界和 HTTP 请求体。服务端需能接收 part_number、total_parts、upload_id 等元信息,并将每个分片按序写入临时文件或对象存储(如 MinIO、S3)。不要依赖单次 http.Request.Body 读完整个大文件——它会吃光内存。
- 客户端必须自行按固定大小(如 5MB)切分文件,计算每个分片的
offset和length - 每个分片请求应带唯一标识(如
X-Upload-ID)和序号(X-Part-Number),便于服务端校验顺序与完整性 - 服务端收到分片后,建议先写入磁盘临时目录(如
/tmp/upload_abc123_part2),而非内存缓存;合并时用io.Copy拼接,避免全量加载
用 io.Seeker 和 io.LimitReader 安全读取分片
直接 io.ReadAll(r.Body) 处理分片极易触发 OOM。正确做法是把 http.Request.Body 当作可寻址流,配合 io.LimitReader 限定本次读取上限,并用 os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_APPEND) 追加写入对应分片文件。
- 服务端解析
X-Part-Number后,生成分片路径:fmt.Sprintf("%s_part%d", uploadID, partNum) - 用
io.CopyN(dst, r.Body, int64(partSize))或io.Copy(dst, io.LimitReader(r.Body, int64(partSize)))控制写入字节数 - 务必校验实际写入长度是否等于期望分片大小(最后一片除外),不一致则返回
400 Bad Request
合并分片时注意文件权限、顺序和原子性
所有分片接收完毕后,不能简单用 cat part1 part2 > final —— Go 程序里要自己按序打开、拼接、关闭。更关键的是:最终文件写入必须是原子的,否则并发请求可能读到半成品。
- 合并前检查分片数量是否等于
total_parts,并验证每个part_{n}文件存在且非空 - 用
os.CreateTemp("", "merge_*.tmp")创建临时目标文件,合并完成后os.Rename(tempPath, finalPath)替换原文件 - 若使用对象存储(如 MinIO),调用
ComposeObjectAPI 比下载再上传更高效,避免中间落盘
前端传参和错误处理必须对齐后端约定
常见坑是前端 JS 用 slice() 切 Blob 时未对齐后端期望的字节边界,或漏传 Content-Length 导致服务端无法判断分片大小。HTTP 状态码也要明确:200 表示单片接收成功,201 表示合并完成,409 表示 upload_id 冲突,416 表示偏移越界。
立即学习“go语言免费学习笔记(深入)”;
- 前端必须在每个分片请求 header 中设置:
X-Upload-ID、X-Part-Number、X-Total-Parts、Content-Length - 服务端对缺失 header 返回
400 Missing required header,对非法part_number返回400 Invalid part number - 上传中断后,前端应能通过
GET /upload/{id}/status查询已上传分片列表,跳过重传









