必须先调用 r.parsemultipartform,否则 r.formfile 和 r.formvalue 均返回空或 nil;go 对 multipart/form-data 采用懒解析,不主动读取 body,所有字段和文件均不可见,且无错误提示,易导致静默失败。

不调用 r.ParseMultipartForm 就直接用 r.FormFile 或 r.MultipartForm,一定拿不到数据——这不是前端没传,是 Go 故意不解析,直到你明确说“我要解析”。
必须先调 r.ParseMultipartForm,否则所有字段和文件都不可见
Go 的 *http.Request 对 multipart/form-data 采用懒解析:body 不主动读,r.Form、r.MultipartForm、r.FormFile 全为 nil 或空。漏掉这步,r.FormFile("file") 返回 (nil, http: no such file),r.FormValue("name") 返回空字符串——日志里看不到错误,但逻辑就卡死了。
-
r.ParseForm()只处理application/x-www-form-urlencoded,对 multipart 完全无效 - 参数不是“总上传大小限制”,而是“内存缓冲上限”:非文件字段值 + 小文件内容最多在内存存多少字节;超限部分自动流式写入磁盘临时文件(路径由
os.TempDir()决定) - 别设
0(等价于math.MaxInt64):攻击者构造巨量小字段就能吃光内存 - 只传纯文本字段?设
1 (1MB)足够;含文件上传?按业务最大预期设,比如 <code>32 (32MB)
r.FormFile 和 r.MultipartForm.File 别混用
r.FormFile("files") 是快捷封装,内部会帮你触发 ParseMultipartForm(如果还没调过),但它只返回同名字段里的第一个 *multipart.FileHeader。如果你的 HTML 是 <input type="file" name="files" multiple>,用户选了 3 个文件,r.FormFile 只给你第一个,其余两个静默丢失。
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
- 多文件场景必须先调
r.ParseMultipartForm,再访问r.MultipartForm.File["files"]——它是个切片,长度就是实际上传个数 - 遍历时务必检查切片是否为空:
if len(files) == 0,否则range会跳过 -
r.MultipartForm.File返回的是[]*multipart.FileHeader,每个元素需调用.Open()才能拿到可读流 - 已解析后再调
r.FormFile,会返回(nil, err)(因为 body 已读完)
保存前必须校验文件名、大小、类型,且不能依赖客户端输入
攻击者可伪造 ../../etc/passwd 这类路径遍历文件名,或把 Content-Length 设得极大但实际发空包,不校验会导致任意文件写入或磁盘爆满。
- 用
filepath.Base(h.Filename)提取原始文件名,丢弃全部路径部分 - 检查
h.Size:例如if h.Size > 10 (10MB)就拒绝 - 限制扩展名或检查 MIME 类型(用
part.Header.Get("Content-Type")或读取前几字节) - 生成服务端唯一文件名(如
uuid.New().String() + filepath.Ext(h.Filename)) - 保存前确保目标目录存在:
os.MkdirAll("./uploads", 0755),否则os.Create失败
大文件上传要防超时、防 OOM、防磁盘写失败
默认 HTTP server ReadTimeout 通常为 30 秒,上传几百 MB 文件极易超时;若把整个文件读进内存(如 io.ReadAll(file)),会吃光 RAM。
- 设置更长的超时:
http.Server{ReadTimeout: 5 * time.Minute, WriteTimeout: 5 * time.Minute} - 用
io.Copy(dst, file)流式写入磁盘,避免中间缓冲 - 设太小的
maxMemory(如1 即 1KB)会导致稍大文件频繁落盘;一旦磁盘写失败(权限不足、空间满、<code>/tmp不可写),后续file.Open()可能返回io.ErrUnexpectedEOF,而不是更直观的磁盘错误 - 真正防恶意超大请求,得靠
http.MaxBytesReader包裹r.Body,或在中间件里检查r.ContentLength
最易被忽略的点:没调 r.ParseMultipartForm 时出问题,日志里既没 panic 也没 error,只是所有字段为空——这种“静默失败”会让排查拖上半天。还有就是多文件上传时死守 r.FormFile,结果永远只拿到第一个。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










