必须手动构造 multipart.Writer,禁用 http.PostFile:后者将整个文件加载进内存易致OOM,不支持自定义Content-Type、额外字段及超时;正确做法是用 multipart.NewWriter 生成合法 boundary,先 WriteField 再 CreateFormFile,按序写入并用 io.Copy 流式传输,结尾调 Close,且 Content-Type 必须设为 w.FormDataContentType() 返回的完整字符串。

必须手动构造 multipart.Writer,别用 http.PostFile
http.PostFile 是个过时的快捷函数,它会把整个文件读进内存再发出去,上传 100MB 文件就可能触发 OOM。它还不支持添加额外字段、自定义 Content-Type、设置超时,也不返回 *http.Response 供你检查状态码。
真正可用的方式是用 multipart.Writer 边写边发:
-
multipart.NewWriter自动产生合法 boundary,别手写字符串(含空格或换行会直接解析失败) - 先调
w.WriteField("user_id", "123")写文本字段,再调w.CreateFormFile("avatar", "photo.jpg")写文件——顺序错可能导致某些后端框架丢弃文件 - 文件内容必须用
io.Copy(fw, file)流式写入,不能io.ReadAll或file.Read全部加载 - 务必在发送前调
w.Close(),否则结尾 boundary 缺失,后端收不到完整 multipart
Content-Type 头必须设为 w.FormDataContentType()
只写 req.Header.Set("Content-Type", "multipart/form-data") 没用,后端根本收不到数据。boundary 是动态生成的,必须用 w.FormDataContentType() 返回的完整字符串,例如 multipart/form-data; boundary=abcdef123456。
漏掉这步最典型的现象是:请求发出去了,服务端 r.ParseMultipartForm 返回 http.ErrNotMultipart,日志里只看到空表单。
立即学习“go语言免费学习笔记(深入)”;
常见错误写法:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
req.Header.Set("Content-Type", "multipart/form-data") // ❌ 错!没 boundary
req.Header.Set("Content-Type", w.FormDataContentType()) // ✅ 对
多文件上传要多次调 CreateFormFile,不是传切片
如果前端是 <input type="file" name="images" multiple>,后端想收 3 个文件,不能只调一次 w.CreateFormFile("images", "a.jpg") 然后往里塞三个文件流——CreateFormFile 每次只创建一个 part。
正确做法是循环调用:
- 对每个文件,都执行一次
w.CreateFormFile("images", filename) - 每次拿到的
fw io.Writer都单独io.Copy(fw, f) - 字段名完全一致(如都是
"images"),后端才能通过r.MultipartForm.File["images"]拿到长度为 3 的切片
用 [] 后缀(如 "images[]")命名字段没意义,Go 标准库不识别这种数组语法,后端得自己 parse 字符串。
超时和上下文控制不能省
大文件上传容易卡住,比如网络抖动或对方服务无响应。不加超时,goroutine 就一直挂着,连接不释放。
必须用 context.WithTimeout 包裹整个流程:
- 创建
http.Client时传入带 timeout 的 context - 用
http.NewRequestWithContext(ctx, ...)替代http.NewRequest - 避免在
io.Copy期间无限等待:可封装带 cancel 的 reader,或依赖 client.Transport 的ResponseHeaderTimeout
最容易被忽略的是:即使设置了 context timeout,如果 io.Copy 正在阻塞读磁盘文件(比如 NFS 挂载点卡住),timeout 也不会生效——此时需配合 time.AfterFunc 主动 close file 或 cancel request。

















