go 的 http.fileserver 不处理文件上传,需手动调用 r.parsemultipartform(32

Go 的 http.FileServer 不处理上传,必须用 ParseMultipartForm 解析请求体,否则 r.FormFile 返回 nil 或报 http: no such file。
必须显式调用 ParseMultipartForm 才能拿到文件
很多人写完 handler 直接调 r.FormFile("file") 却拿不到内容,根本原因是没触发 multipart 解析。Go 不会自动解析 multipart/form-data 请求体,必须手动调用:
r.ParseMultipartForm(32 (即 32MB 内存上限),这是最安全的起点- 不调用它,
r.FormFile、r.MultipartForm.File全是空的,r.FormValue只能取到文件名字符串,不是文件内容 - 如果设为
0,所有数据都会进内存,大文件直接 OOM;不设参数默认是 32MB,但显式写出来更清晰
前端表单 enctype 属性不能漏
HTML 表单必须带 enctype="multipart/form-data",否则浏览器按 application/x-www-form-urlencoded 发送,服务端收不到 boundary,ParseMultipartForm 会静默失败。
- 正确写法:
<form method="POST" enctype="multipart/form-data"><input type="file" name="avatar"></form> - 字段名
name="avatar"必须和后端r.FormFile("avatar")中的字符串完全一致 - 多文件上传加
multiple属性,后端用r.MultipartForm.File["files"]拿切片
文件保存前必须校验三件事
只靠 filepath.Ext(header.Filename) 或 header.Header.Get("Content-Type") 判断类型极危险——攻击者可伪造 filename="shell.php" 并声称是 image/jpeg。
- 用
http.DetectContentType读文件头前 512 字节,识别真实类型(如jpeg、png) - 扩展名走白名单:
map[string]bool{"jpg": true, "png": true, "pdf": true},拒绝php、js、html - 生成唯一文件名:
uuid.New().String() + ".jpg",彻底丢弃原始Filename,防路径遍历(如../../etc/passwd) - 保存前确保目录存在:
os.MkdirAll("./uploads", 0755),别用0777,也别把 uploads 放在 Web 根目录下
大文件要绕过 ParseMultipartForm 的内存陷阱
默认 ParseMultipartForm 对大文件仍会先缓冲再转磁盘,有延迟和 fd 泄露风险。真正稳的做法是跳过它,直接流式读取:
- 用
r.MultipartReader()获取*multipart.Reader,然后循环NextPart() - 对每个
part,检查part.FormName() == "file",再用io.Copy边读边写入磁盘或对象存储 - 务必调
part.Close()和mr.Close(),否则文件描述符泄漏 - 配合
http.MaxBytesReader(w, r.Body, maxSize)在读 body 阶段就拦截超限请求,比等ParseMultipartForm报错更早止损
最容易被忽略的是:上传目录权限、文件头检测、以及流式读取时的 Close() 调用——这三个点出问题,轻则文件损坏,重则服务器被写入 webshell 或 fd 耗尽挂掉。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











