文件上传需用 c.FormFile 获取,因其自动调用 ParseMultipartForm;手动读 c.Request.Body 会跳过该步骤导致失败;必须使用 POST + enctype="multipart/form-data",且服务端需设置 ParseMultipartForm 最大内存限制。

文件上传:用 c.FormFile 拿到文件,别直接读 c.Request.Body
上传失败或报 http: no such file,大概率是没走表单解析流程。Gin 的 c.FormFile 会自动调用 ParseMultipartForm,而手动读 c.Request.Body 会跳过这步,导致后续 c.FormFile 返回空。
- 必须用
POST+enctype="multipart/form-data",前端不能发 JSON - 服务端要设最大内存限制:
c.Request.ParseMultipartForm(32 (32MB),否则超限直接 400 -
c.FormFile("file")返回*multipart.FileHeader,不是文件内容;要用header.Open()才能读流 - 别在 handler 里用
io.Copy直接写磁盘——出错没回滚,建议先os.CreateTemp再 rename
file, err := c.FormFile("file")
if err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": err.Error()})
return
}
src, _ := file.Open()
defer src.Close()
dst, _ := os.Create("/tmp/" + file.Filename)
defer dst.Close()
io.Copy(dst, src)
文件下载:用 c.Header 设 Content-Disposition,别只靠 c.File
浏览器打不开、提示“无法加载”或自动保存成乱码名,通常是响应头缺失或不匹配。Gin 的 c.File 只设了 Content-Type 和 Content-Length,但下载行为由 Content-Disposition 控制。
- 强制下载必须加:
c.Header("Content-Disposition", "attachment; filename=\""+filename+"\"") - 中文文件名要 URL 编码:
url.PathEscape(filename),否则 Chrome 拒绝解析 - 如果文件路径来自用户输入,务必校验路径是否在白名单目录内,防止
../路径穿越 - 大文件别用
c.File——它会全量读入内存;改用c.DataFromReader流式传输
c.Header("Content-Disposition", "attachment; filename="+url.PathEscape("报告.pdf"))
c.Header("Content-Type", "application/pdf")
c.DataFromReader(200, f.Size(), "application/pdf", f, nil)
Gin 静态资源:用 c.StaticFS 或 gin.Static,别把前端构建产物放 ./ 下硬编码引用
页面 404、CSS/JS 加载失败,常见原因是静态路径没注册,或者用了相对路径但路由嵌套后 base 不对。Gin 默认不提供任何静态服务,必须显式挂载。
-
router.Static("/static", "./assets")表示所有/static/xxx请求映射到本地./assets/xxx - 如果前端是 SPA(如 Vue/React),需配合
router.NoRoute返回index.html,否则刷新子路由 404 - 生产环境别用
router.LoadHTMLFiles加载模板——它不支持热更新且无缓存控制;改用router.LoadHTMLGlob+ETag头 - 静态资源路径不要写死在 HTML 里,比如
/css/app.css;应统一用/static/css/app.css并确保前缀一致
上传+下载+静态资源共存时,权限与路径隔离最容易被忽略
上传目录和静态目录混在一起,或者下载接口没校验文件是否存在,就可能让攻击者通过构造 filename=../../../etc/passwd 下载系统文件。Gin 不做路径净化,这事得自己兜底。
立即学习“go语言免费学习笔记(深入)”;
- 上传文件名必须清洗:
filepath.Base(filename)去掉路径部分,再加白名单后缀校验(如只允许.pdf,.png) - 下载接口的文件路径必须用
filepath.Join(uploadDir, cleanName)构造,不能拼字符串 - 静态资源目录和上传目录物理隔离,比如
/var/www/static和/var/www/uploads,别共用父目录 - 上传后生成的访问 URL 不要直接暴露真实路径,用带签名的临时链接或代理转发更安全
路径处理那几行看着简单,但漏掉一个 filepath.Clean 或少一次 Base,就可能变成 RCE 入口。别信“我这没人会传恶意文件”。










