直接用 multipart.formfile 无法支持断点续传,因其读取完整上传流且不暴露原始字节流边界;需禁用 gin 自动绑定,用 http.maxbytesreader 包装 request.body 后手动解析 multipart boundary。

为什么直接用 multipart.FormFile 无法支持断点续传
因为 multipart.FormFile 读取的是完整上传流,一旦连接中断,整个请求就失败,服务端拿不到任何分块数据;而且它不暴露原始字节流边界,无法校验分块序号、MD5 或合并逻辑。断点续传必须绕过表单解析,直接读取 http.Request.Body 原始数据,并自行解析 multipart boundary。
如何用 Gin 获取原始 multipart body 并解析分块
Gin 默认会预读 Request.Body 来做绑定和日志,这会导致后续读取为空。必须在中间件或路由 handler 开头调用 c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxBodySize) 并禁用自动绑定(如不用 c.ShouldBind),再用 mime/multipart 手动解析:
- 在路由前加
c.Request.ParseMultipartForm(32 不起作用——它只影响 <code>FormFile,不影响原始 Body - 正确做法:调用
reader, err := multipart.NewReader(c.Request.Body, boundary),其中boundary从c.GetHeader("Content-Type")中提取,正则匹配boundary=(.+) - 每个
part需检查part.Header.Get("X-File-Name")、X-Chunk-Index、X-Total-Chunks等自定义 header,而不是依赖 form key 名 - 分块内容建议用
io.Copy直接写入临时文件(如/tmp/upload_${fileId}_${index}.part),避免全量加载进内存
怎么安全地管理分块文件的生命周期和合并时机
不能等所有分块都到齐才合并——客户端可能崩溃、网络超时、重复上传同一分块。需要引入状态跟踪:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 用 Redis 存储
upload_id → {total: N, received: [1,3,4], expired_at: ts},每次收到分块就SADD索引并更新过期时间(如 2 小时) - 合并操作必须加分布式锁(
SET upload_id:lock "1" EX 30 NX),防止并发合并导致文件损坏 - 合并时按索引顺序读取各
.part文件,用os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_APPEND)追加写入目标文件,不要用os.Rename替换——部分写入失败会导致丢数据 - 合并成功后,用
os.Remove清理所有.part文件,并删除 Redis 中对应 key
常见错误:分块 MD5 校验失败或合并后文件损坏
多数问题出在 multipart 解析边界处理或文件写入模式上:
-
multipart.Reader.NextPart()返回的part.Body是带 CRLF 的原始内容,但实际分块 payload 不含开头的--boundary\r\n...和结尾的\r\n——这部分由multipart自动剥离,无需手动 trim - 如果客户端用
fetch+FormData.append("file", blob, name),Gin 收到的Content-Type是multipart/form-data; boundary=xxx,但某些 SDK(如 axios)可能漏传 boundary,需 fallback 到c.Request.MultipartReader()自动推导 - 合并时若用
os.Create覆盖写入,而某个分块写入中途失败,会导致目标文件被截断;必须先写入.tmp后缀文件,确认全部写完再os.Rename - Linux 下注意
ulimit -n限制,大量并发分块写入可能触发 “too many open files”,建议用sync.Pool复用*os.File或控制并发数
真正麻烦的不是分块接收,而是怎么在节点宕机、网络抖动、客户端重试下保证原子性和幂等性——Redis 锁 + 本地临时文件 + 状态轮询缺一不可。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










