ctx.Download() 是 Fiber 实现单文件下载最直接方式,自动设 Content-Disposition: attachment 并流式写入,避免内存加载;需校验用户输入路径防遍历,大文件须监听 c.Context().Done() 防 goroutine 泄漏。

怎么用 ctx.Download() 实现单文件下载
Fiber 没有“下载路由”这个概念,只有“返回文件并设置响应头触发浏览器下载”的处理逻辑。ctx.Download() 是最直接的方式:它会自动设置 Content-Disposition: attachment,并读取本地文件流式写入响应体,不加载全量到内存。
常见错误是直接用 c.SendFile() —— 它默认设的是 inline,浏览器优先预览而非下载;或者手动拼 Content-Disposition 但漏掉 filename*=UTF-8 导致中文名乱码。
-
c.Download("report.pdf"):从当前工作目录查找,适合固定路径小文件 -
c.Download("./uploads/invoice_20261009.pdf"):推荐用绝对路径或基于配置的路径变量,避免相对路径不确定性 - 如果文件名来自用户输入(如
c.Query("file")),必须校验和白名单过滤,禁止路径遍历:filepath.Clean()+ 前缀检查(如确保以"./downloads/"开头)
为什么 ctx.Attachment() 不是下载入口
ctx.Attachment() 只负责设置响应头,不读文件也不写响应体。它常被误当作“下载方法”,但实际必须配合 c.SendFile() 或 c.Stream() 才能生效,否则响应为空。
典型翻车写法:c.Attachment("log.txt"); c.SendString("done") → 浏览器收到文本响应,不是下载。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 正确组合:
c.Attachment("log.txt"); c.SendFile("./logs/log.txt") - 更简洁等价于:
c.Download("./logs/log.txt")(内部就是这两步合并) - 若需动态生成内容(非磁盘文件),用
c.Stream()+ 手动Attachment头
大文件下载要注意 ctx.Context().Done() 和超时
Fiber 的 c.Context() 继承自 context.Context,客户端断连时会触发 Done()。对几十 MB 以上的文件,不监听可能造成 goroutine 泄漏或卡死连接。
尤其当后端存储是网络盘(如 S3)、或用了 io.Copy 转发流时,必须检查 err == context.Canceled 并提前退出。
- 用
c.Context().Done()判断中断:select { case - 避免在
Download()前调用c.Set("Content-Length", ...)—— 它会禁用 chunked 编码,导致无法流式传输且长度不准 - 如果文件路径不确定存在,
Download()会返回ErrNotFound,记得return防止后续逻辑执行
路由注册时路径结尾斜杠会影响下载吗
不影响。下载行为由 handler 决定,和路由匹配无关。但 StrictRouting 设置会影响你能否命中该 handler。
比如注册了 app.Get("/download/:name", h),访问 /download/report.pdf/(带尾部斜杠)会 404 —— 不是因为下载逻辑问题,而是根本没进 handler。
- 解决方案不是关
StrictRouting,而是显式加一条:app.Get("/download/:name/", h) - 或统一重定向:
app.Use("/download/*", func(c *fiber.Ctx) error { if strings.HasSuffix(c.Path(), "/") { return c.Redirect(strings.TrimSuffix(c.Path(), "/"), 301) } return c.Next() }) - 别在路径里硬编码文件扩展名(如
/download/pdf/:id),用查询参数或 Content-Type 协商更灵活


















