不能用 r.parsemultipartform() 接收大文件,因其会将整个 multipart body 全部加载进内存再按 maxmemory 切分,导致大文件上传时直接触发“http: request body too large”错误,无法进入后续文件处理逻辑。

为什么不能用 r.ParseMultipartForm() 接收大文件
它会把整个 multipart body(含所有文本字段 + 文件)先加载进内存,再按 MaxMemory 限制切分——哪怕你只传一个 2GB 文件 + 3 个字符串字段,也会在解析阶段就触发 http: request body too large 错误,根本到不了文件处理逻辑。Gin 的 c.Request.FormFile() 底层就是它,同样踩坑。
必须用 multipart.NewReader() 手动流式解析
绕过表单解析,直接从 r.Body 提取 boundary 后构建 reader,逐 part 处理:
- 调用
part.FormName()判断是文件还是普通字段;只对part.FormName() == "file"的 part 做后续操作 - 用
io.CopyBuffer(dst, part.Body)写入磁盘或对象存储,避免一次性读满内存 - 每个
part.Close()和最终mr.Close()必须调用,否则 fd 泄露、后续请求卡死 - 临时文件路径必须可控——别依赖系统
/tmp,自己拼upload_tmp/{upload_id}/{chunk_index}
分片上传时如何保证写入位置与顺序
客户端按固定大小(如 5MB)切片,并通过 query 或 header 传 upload_id、chunk_index、total_chunks,服务端绝不从 body 解析这些元数据:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 接收时用
os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)追写,而非覆盖 - 多 goroutine 并发写同一 upload_id 下不同 chunk 时,无需锁——因为 offset 固定、路径唯一
- 但合并前必须校验是否存在全部
0..N-1分片,缺一片就返回400 Bad Request - 合并用
io.Copy顺序打开每个分片文件追加,最后重算总哈希,不匹配就全删
并发上传下如何防 goroutine 爆炸和连接耗尽
起 100 个 go upload() 看似简单,实际会迅速打爆本地端口、触发 DNS 超时、被限流 429,甚至让 http.Client 连接池瘫痪:
- 全局复用一个
http.Client,设置Transport.MaxIdleConnsPerHost = 100(默认仅 2) - 用带缓冲 channel 做并发令牌:
sem := make(chan struct{}, 10),每任务先sem ,完成后 <code> - 每个请求必须带
context.WithTimeout(ctx, 5 * time.Minute),超时后自动 cancel body 流 - 服务端反向代理转发分片请求时,用
307 Temporary Redirect,别用http.Post—— body 是单次流,转发前已关闭
真正麻烦的不是代码怎么写,而是分片元数据怎么存、失败后状态怎么恢复、临时文件生命周期怎么管理——磁盘暂存不可靠,但全放 Redis 又容易 OOM;用 etcd 存状态能持久,但 watch 开销得算清楚。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










