r.formvalue() 能正确获取 multipart/form-data 中的普通字段和文件,而 r.postformvalue() 总为空,因其依赖的 r.postform 在 multipart 请求中不会被 r.parseform() 填充。

直接用 r.FormValue() 读普通字段,用 r.FormFile() 或 r.MultipartForm.File 拿文件 —— 不要依赖 r.PostFormValue(),它对 multipart/form-data 完全无效。
为什么 r.PostFormValue() 总是空
因为 r.PostForm 只在 r.ParseForm() 调用后填充,而该函数对 multipart/form-data 请求直接跳过解析(返回 nil error,但不填数据)。r.PostFormValue("key") 实际查的是空的 r.PostForm,必然为空。
r.FormValue("key") 则不同:它会先检查是否已解析 multipart,若未解析且请求是 multipart,就自动调用 r.ParseMultipartForm(32 (默认 32MB 内存上限),再从 <code>r.Form 中取值。所以它能兜住两种编码类型。
- ✅ 推荐统一用
r.FormValue()处理所有表单字段(文本、隐藏域、下拉框等) - ❌ 禁止在含
enctype="multipart/form-data"的表单中用r.PostFormValue() - ⚠️ 即使你只提交文本字段但声明了
multipart/form-data,r.PostFormValue()依然为空
如何安全获取上传的文件
必须显式调用 r.ParseMultipartForm(maxMemory),否则 r.FormFile() 和 r.MultipartForm 都是 nil。
maxMemory 是关键参数:它控制多少字节以内存方式缓存,超出部分写临时文件。设太小(如 1KB)会导致小文件也刷盘;设太大(如 512MB)可能 OOM。常见值是 10 (10MB)或 <code>32 (32MB)。
-
file, header, err := r.FormFile("uploadFile")—— 适合单个文件,自动处理解析和打开 -
fileHeaders := r.MultipartForm.File["uploadFile"]—— 适合多文件(如name="files[]"),返回切片,需手动遍历Open() - 务必用
defer file.Close(),否则文件句柄泄漏 - 用
filepath.Base(header.Filename)提取文件名,防止../../etc/passwd类路径穿越
处理多文件或数组型字段(如 name="files[]")
HTML 中用 name="files[]" + multiple 属性可一次选多个文件,但 Go 不会自动转成 slice —— r.FormFile("files[]") 只返回第一个,其余被忽略。
正确做法是绕过 FormFile,直取 r.MultipartForm.File:
err := r.ParseMultipartForm(10
- 注意:必须先调用
ParseMultipartForm,否则r.MultipartForm为 nil -
r.MultipartForm.File是map[string][]*multipart.FileHeader,键是 HTML 中的name值 - 如果前端用
name="files"(无[])但加了multiple,浏览器仍会发多个同名 part,Go 也存进r.MultipartForm.File["files"]切片里
最易被忽略的一点:即使你只调了一次 r.FormValue() 触发了自动解析,后续调 r.FormFile() 仍可能失败 —— 因为 FormFile 内部会再次检查 r.MultipartForm 是否就绪,而某些 Go 版本在自动解析后未完全初始化文件句柄。稳妥起见,显式调一次 ParseMultipartForm 是底线操作。











