文件上传必须手动解析 multipart,禁用 c.FormFile();因其默认将整个文件读入内存导致 OOM 或 400 错误,应显式调用 ParseMultipartForm 并设合理内存阈值。

文件管理系统后台不能靠 ORM 存文件本体,也不能用 c.File() 直接吐大文件——前者爆数据库、后者卡死中间件链。核心就一条:元数据走 DB,文件流绕开框架直写磁盘或对象存储。
上传接口必须手动解析 multipart,不能依赖 c.FormFile()
很多人写 c.FormFile("file") 就完事,结果上传 200MB 文件时服务直接 OOM 或返回 400 却没提示。因为 c.FormFile() 内部调用 ParseMultipartForm,默认把整个文件读进内存。
- 显式调用
c.Request.ParseMultipartForm(32 (即 32MB 上限),并检查是否返回 <code>http.ErrRequestTooLarge - 拿到
multipart.File后,用io.Copy流式写入磁盘,别io.ReadAll:
src, _ := c.FormFile("file")
dst, _ := os.Create("/data/uploads/" + src.Filename)
defer dst.Close()
io.Copy(dst, src)
同时建议在写入时同步计算 SHA256,存入数据库的 FileMeta.Hash 字段,用于去重和校验。
下载路径必须脱离 Gin 中间件链
用 c.File("/data/uploads/xxx.pdf") 是危险操作:它会触发完整中间件链(JWT 校验、日志、panic recover),不仅慢,还可能因权限中间件误判返回 401 —— 你只是想公开提供一个静态资源。
立即学习“go语言免费学习笔记(深入)”;
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 注册独立的
http.ServeMux子路由,不经过 Gin router:
fs := http.FileServer(http.Dir("/data/uploads"))
router.Any("/download/*filepath", func(c *gin.Context) {
c.Writer.Header().Set("Content-Type", "application/octet-stream")
http.StripPrefix("/download", fs).ServeHTTP(c.Writer, c.Request)
})
注意路径前缀要一致,且 Content-Type 显式设为 application/octet-stream,避免浏览器尝试渲染二进制内容。
数据库只存元数据,不做文件本体字段
常见错误是定义 type FileMeta struct { Name string; Data []byte `gorm:"type:longblob"` },然后 db.Create(&f) —— 这会导致 MySQL 报 ERROR 1153 (08S01): Got a packet bigger than 'max_allowed_packet',且备份膨胀、查询变慢。
- 结构体里只保留路径、哈希、大小、归属用户 ID、创建时间等字段,例如:
Path string `gorm:"index"`、Size int64、Hash string - 真实文件写磁盘(如
/data/uploads/abc123.pdf)或对象存储(MinIO),DB 只记路径 - 如果要用 GORM 查询带关联信息(比如用户+文件列表),用
Preload防 N+1,别在循环里查f.User.Name
权限校验和路由隔离要分层处理
不是所有文件都能公开下载,也不是所有上传都无限制。但权限逻辑不能混在文件流处理里——那会让 io.Copy 被阻塞。
- 上传前先做权限判断:检查用户角色、配额、扩展名白名单(如禁止
.exe)、文件名 sanitization(去掉../) - 下载路由仍走 Gin,但只做鉴权,不碰文件流;鉴权通过后,跳转到上面那个独立的
/download/xxx路由 - 别在
http.FileServer前加 Gin 中间件,那是反模式;真要鉴权,用独立 handler +http.ServeFile或os.Open+io.Copy
最易被忽略的是:上传时的 ParseMultipartForm 上限和下载时的 http.FileServer 路径安全校验,这两处一旦漏掉,轻则服务不稳定,重则目录遍历或磁盘写满。

















