c.File()是最简单直接的静态文件下载方式,自动设置Content-Disposition: attachment触发浏览器下载,但要求路径绝对可信、需白名单校验且禁止拼接用户输入,否则易引发目录遍历漏洞。

用 c.File() 直接返回静态文件下载
最简单的方式是调用 c.File(),它会自动设置 Content-Disposition: attachment 响应头,触发浏览器下载行为。该方法适用于已知绝对路径的本地文件,比如用户上传后保存在服务器上的文件。
注意:c.File() 不做路径安全校验,传入的路径必须可信;若路径含 ../ 或空字节等恶意片段,可能造成目录遍历漏洞。
- 确保文件存在且可读,否则返回 404
- 路径应为服务端绝对路径或相对于当前工作目录的合法路径(如
"./uploads/report.pdf") - 不建议直接拼接用户输入构造路径,哪怕只取文件名也需先净化(如用
filepath.Base()提取 basename)
c.Attachment() 和 c.Blob() 的适用场景区别
c.Attachment() 是 c.File() 的封装变体,接受路径和自定义下载文件名(即 filename 参数),底层仍调用 http.ServeFile;而 c.Blob() 适合从内存或流中生成内容再下载,比如动态生成 PDF、压缩包或加密后的内容。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
-
c.Attachment("path/to/log.zip", "server-log-20261019.zip"):指定原始路径 + 下载时显示的文件名 -
c.Blob(http.StatusOK, "application/zip", data):data是[]byte,适合小文件;大文件建议用c.Stream()避免内存堆积 - 若文件来自
c.FormFile()并需原样下载,不要用c.File()指向临时路径(如/tmp/xxx),因为临时文件可能已被清理——应先fh.Open()读取并用c.Stream()流式返回
大文件下载必须用 c.Stream() 防止 OOM
当文件体积超过几十 MB,或你无法将其完整加载进内存时,c.File() 和 c.Blob() 都会把整个文件读入内存再发给客户端,极易触发 Out of Memory。此时唯一可靠方式是 c.Stream(),它接收一个 func(w io.Writer) error,边读边写,内存占用恒定。
- 典型组合:用
os.Open()打开文件 → 传给io.Copy到c.Response().Writer→c.Stream()封装 - 务必手动设置
Content-Length头(可通过file.Stat().Size()获取),否则浏览器无法显示进度条 - 记得
defer f.Close(),否则高并发下会快速耗尽文件描述符
前端点击链接触发下载时的常见陷阱
如果前端用 <a href="/download?id=123">下载</a> 触发,后端路由必须严格匹配 HTTP 方法(GET)且不能重定向(302),否则部分浏览器会忽略 Content-Disposition: attachment 并渲染响应内容。
- 禁止在 handler 中调用
c.Redirect()后再想下载——重定向后响应已结束 - 避免中间件(如 JWT 验证)意外返回 HTML 错误页,导致下载响应体被污染成 HTML 文本
- 若需权限校验,应在
c.File()前完成,并确保所有错误都用echo.NewHTTPError(http.StatusForbidden, "...")等标准方式返回,而非c.HTML()
真正难处理的不是“怎么让浏览器弹保存框”,而是路径可控性、大文件流控、以及中间件与下载逻辑的生命周期冲突——这三个点漏掉任一个,上线后都可能表现为偶发 500、空文件、或下载中断。


















