必须先调用 r.parsemultipartform(32

如何用 http.HandleFunc 正确接收 multipart/form-data 文件
Go 的 net/http 不会自动解析文件上传,必须显式调用 r.ParseMultipartForm,否则 r.MultipartForm 为 nil,r.FormFile 会直接 panic。
常见错误是只调用 r.ParseForm() —— 它对 multipart 无效,且后续再调 ParseMultipartForm 会报 http: multipart handled 错误。
- 务必在读取任何表单字段或文件前,先调用
r.ParseMultipartForm(32 (32MB 内存上限,超出部分写入临时磁盘) - 若未指定大小,默认仅 32KB 内存缓冲,大文件会静默失败
-
r.FormValue("name")和r.FormFile("file")都依赖ParseMultipartForm的前置执行
如何安全保存上传的文件并避免路径遍历攻击
r.FormFile 返回的 *os.File 是内存或磁盘上的临时文件句柄,但文件名来自客户端,不可信。直接拼接 filepath.Join(uploadDir, filename) 极易被构造为 ../../etc/passwd。
正确做法是丢弃原始文件名,生成服务端可控的唯一标识:
立即学习“go语言免费学习笔记(深入)”;
第一步】:将安装包中所有的文件夹和文件用ftp工具以二进制方式上传至服务器空间;(如果您不知如何设置ftp工具的二进制方式,可以查看:(http://www.shopex.cn/support/qa/setup.help.717.html)【第二步】:在浏览器中输入 http://您的商店域名/install 进行安装界面进行安装即可。【第二步】:登录后台,工具箱里恢复数据管理后台是url/sho
- 用
uuid.New().String()或time.Now().UnixNano()生成新文件名 - 用
path.Base()提取原始名后缀做白名单校验(如只允许.jpg,.pdf) - 保存时强制指定扩展名:
dst, _ := os.Create(filepath.Join(uploadDir, id + ".pdf")) - 永远不要用
filepath.Clean()“修复”客户端路径——它无法阻止所有绕过手段
为什么 r.MultipartForm.File 有时为空或报错 http: no such file
这个错误通常不是文件没传,而是请求体已提前被读取过一次(比如日志中间件调了 ioutil.ReadAll(r.Body)),导致底层 multipart.Reader 流已耗尽。
MultipartForm 是一次性结构:一旦解析完成,r.Body 就不能再读;反之,若没解析就直接读 r.Body,后续 FormFile 必然失败。
- 禁用所有未经处理的
r.Body全量读取(包括调试打印r.Body) - 若需记录原始请求体,应在
ParseMultipartForm前用io.TeeReader复制流到bytes.Buffer - 使用中间件时,确保其兼容
multipart—— 标准http.Request不支持 rewind
如何处理大文件上传并防止 OOM 或超时
默认 HTTP server 没有上传大小和时间限制,大文件可能拖垮内存或阻塞连接。必须主动设限:
- 设置
http.Server.ReadTimeout和WriteTimeout(注意:Go 1.22+ 推荐用ReadHeaderTimeout+IdleTimeout) - 在 handler 开头检查
r.ContentLength,超阈值直接http.Error(r, "too large", http.StatusRequestEntityTooLarge) - 用
io.CopyN(dst, src, maxBytes)替代io.Copy,防止写入失控 - 临时文件默认存在
os.TempDir(),生产环境应显式指定独立磁盘路径,避免 tmp 占满根分区
multipart 解析本身不流式,但文件内容可通过 file.Open() 后逐块读取——真正流式处理得靠自定义 multipart.Reader,不过多数场景用临时文件更稳。









