用 http.handlefunc 接收 multipart/form-data 文件需先调用 r.parsemultipartform 设置 maxmemory,再通过 r.formfile 或 r.multipartform.file 获取文件;不可直接读 r.body 或用 r.formvalue 取文件内容;须校验路径、内存、超时和 mime 类型以防 rce、dos 或信息泄露。
如何用 http.HandleFunc 接收 multipart/form-data 文件
Go 标准库原生支持表单文件上传,关键不是自己解析 raw body,而是调用 r.ParseMultipartForm 触发解析,再从 r.MultipartForm.File 中取文件元数据。不调用这个方法,r.FormFile 会返回 http.ErrNotMultipart。
常见错误是直接读 r.Body 或误以为 r.FormValue 能拿到文件内容 —— 它只能取文本字段。
- 必须设置
MaxMemory(如32 表示 32MB 内存缓冲),否则大文件会直接写临时磁盘,且无错误提示 - 前端
<input type="file">必须在<form enctype="multipart/form-data"></form>内,否则后端收不到文件 -
r.FormFile("file")返回的是*multipart.FileHeader,它不包含文件内容,只是描述信息(如Fh.Filename、Fh.Size、Fh.Header)
如何安全地保存上传的文件到本地磁盘
拿到 *multipart.FileHeader 后,需用 Fh.Open() 打开源流,再用 os.Create 创建目标文件并 io.Copy。重点在于路径拼接和文件名处理 —— 直接使用 Fh.Filename 是严重安全隐患。
- 永远不要用
Fh.Filename作为最终文件名:它可被客户端伪造为../../etc/passwd或带 null 字节 - 推荐做法:生成唯一 ID(如
uuid.New().String()),再根据原始扩展名白名单校验后拼接,例如fmt.Sprintf("%s%s", id, ext),其中ext来自path.Ext(Fh.Filename)且仅限.jpg、.pdf等可信后缀 - 保存前检查目标目录是否存在,用
os.MkdirAll(dir, 0755)创建;避免用os.Create写入系统敏感路径(如/tmp下未隔离的子目录)
为什么 http.ServeFile 不该直接暴露上传目录
上传后的文件若需通过 HTTP 访问,很多人会直接注册 http.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads")))) —— 这会导致路径遍历风险:只要 URL 中含 %2e%2e/(即 ../ 编码),就可能访问到上级目录。