Go HTTP 文件上传需先调用 ParseMultipartForm 设置内存阈值(如32

Go HTTP 文件上传:用 request.ParseMultipartForm 读取 multipart/form-data
Go 标准库不自动解析 multipart 表单,必须显式调用 ParseMultipartForm,否则 request.FormFile 会返回 nil, http.ErrNotMultipart。
- 务必在调用
FormFile前设置内存阈值:err := r.ParseMultipartForm(32 << 20) // 32MB 内存上限,超限写入临时文件
-
r.FormFile("file")返回*multipart.FileHeader,它不是文件内容,只是元信息(Filename、Size、Header) - 真正读取内容需用
header.Open()得到io.ReadCloser,且必须手动Close() - 生产环境务必校验
header.Size和header.Filename(防空名、路径遍历如../../etc/passwd)
Go 文件保存:避免直接拼接路径,用 filepath.Join + os.Create
用户传的 Filename 可能含恶意路径,os.Create("upload/" + header.Filename) 会导致任意目录写入。
- 安全做法:提取纯文件名,忽略原始路径
filename := filepath.Base(filepath.Clean(header.Filename))
- 创建完整路径并确保父目录存在:
dstPath := filepath.Join("uploads", filename) if err := os.MkdirAll(filepath.Dir(dstPath), 0755); err != nil { /* handle */ } - 用
os.Create而非ioutil.WriteFile(后者无法流式处理大文件),配合io.Copy:dst, err := os.Create(dstPath) if err != nil { /* handle */ } defer dst.Close() if _, err := io.Copy(dst, src); err != nil { /* handle */ }
Go 文件下载:用 http.ServeFile 或手动设置 Header + io.Copy
http.ServeFile 简单但暴露绝对路径,且无法做权限校验;手动方式更可控。
- 手动下载需设三个关键 Header:
w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename)) w.Header().Set("Content-Length", strconv.FormatInt(fileInfo.Size(), 10)) - 用
os.Open打开文件后,必须检查fileInfo.Size()是否与实际一致(防止竞态或截断) - 推荐用
io.Copy流式传输,避免全量加载到内存:file, _ := os.Open(filePath) defer file.Close() io.Copy(w, file)
- 若文件路径由用户输入,必须用
filepath.Clean并校验是否在白名单目录内(如strings.HasPrefix(absPath, allowedRoot))
Web 集成时的常见陷阱:MIME 类型误判、并发写冲突、临时文件残留
Go 的 ParseMultipartForm 默认将大文件写入 /tmp,但不会自动清理——上传失败或 panic 后临时文件滞留。
立即学习“go语言免费学习笔记(深入)”;
- 不要依赖浏览器发送的
Content-Type(可伪造),用filetype库或net/http.DetectContentType检查前 512 字节 - 多个请求同时写同一文件名?加锁或用唯一 ID(如
uuid.NewString()+ 原始名) - 临时文件清理:注册
defer os.Remove(header.Filename)不可靠(路径不对);正确做法是用header.Open()后拿到的*os.File对象,在读完后调用file.Close(),系统会自动清理其关联的临时文件 - 上传进度不可见?标准
net/http不支持;需改用gobuffalo/packr/v2或自定义中间件包装http.Request.Body
文件上传下载本身不难,难的是边界校验和资源生命周期管理——尤其是临时文件何时删、锁粒度怎么控、错误路径下 Close 是否被遗漏。这些地方一漏,服务跑几天就磁盘告警。










