ctx.attachment() 是 fiber 框架中发送文件下载的最常用方式,它自动设置 content-disposition: attachment 响应头并推导 content-type,但仅支持本地文件路径(需确保存在、可读且用绝对路径),不支持字节流;中文文件名默认 rfc 5987 编码,大文件慎用(无断点续传),内存生成文件应改用 ctx.sendstream() 配合手动设头。

如何用 ctx.Attachment() 发送文件给浏览器
直接调用 ctx.Attachment() 是最常用也最稳妥的方式,它会自动设置 Content-Disposition: attachment 响应头,并推导出合适的 Content-Type。注意:该方法只接受本地文件路径(string),不支持字节流或 io.Reader。
常见错误是传入相对路径但没注意工作目录——Fiber 启动时的当前目录不一定是项目根目录,建议用 filepath.Abs() 或硬编码绝对路径。
- 确保文件存在且 Web 进程有读权限,否则返回 500 错误,日志里可能只显示
"open xxx: no such file or directory" - 如果文件名含中文,
ctx.Attachment()默认使用 RFC 5987 编码,现代浏览器基本兼容;但旧版 IE 可能乱码,可手动设ctx.Set("Content-Disposition", `attachment; filename*=UTF-8''` + url.PathEscape(name)) - 大文件(>100MB)慎用,因为
Attachment()内部会调用http.ServeFile,不支持断点续传,也不走SendFile零拷贝优化
想用内存生成文件(如 Excel/PDF)并下载怎么办
不能直接传 []byte 给 Attachment(),得换用 ctx.SendStream() + 手动设头。核心是三步:设 Content-Type、设 Content-Disposition、用 SendStream 推送数据流。
示例场景:用 excelize 生成报表后下载:
buf := &bytes.Buffer{}
f := excelize.NewFile()
// ... 写入数据
f.Write(buf)
ctx.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
ctx.Set("Content-Disposition", `attachment; filename="report.xlsx"`)
ctx.SendStream(buf)
- 务必在
SendStream()前调用Set(),顺序反了会导致响应头丢失 -
SendStream()不校验内容长度,若流提前 EOF,浏览器可能提示“文件损坏”——确保io.Reader稳定返回完整数据 - 避免用
ctx.Status(200).JSON(...)混用,HTTP 响应体只能有一种类型
如何支持断点续传和大文件高效传输
Fiber 默认不开启 SendFile 的零拷贝模式,需显式启用且依赖底层 OS 支持(Linux sendfile,macOS sendfile,Windows TransmitFile)。对超大静态文件(如视频、安装包),这是关键优化点。
- 启用方式:启动 Fiber 时加选项
fiber.Config{EnablePrintRoutes: true, ServerHeader: "Fiber"}不影响,真正起作用的是ctx.SendFile(path, fiber.Map{"gzip": false})中的gzip参数设为false(否则禁用sendfile) - 必须配合
ctx.Attachment()或手动设Content-Disposition,SendFile()本身只管传输,不改 disposition - 注意:
SendFile()要求路径为真实文件系统路径,且不能是符号链接(某些版本会拒绝)
为什么下载的文件总是打不开或大小为 0
绝大多数情况是响应头和响应体不匹配,或者中间件干扰了写操作。典型诱因包括:Logger 中间件捕获了 panic 却没透出错误、自定义 Recover 中间件吞掉了 write 失败、或在 ctx.SendXXX() 后又调用了 ctx.JSON()。
- 检查是否在
Attachment()/SendStream()后继续执行其他写操作——Fiber 不阻止,但 HTTP 连接已提交,后续写入会被忽略或触发"http: response.WriteHeader on hijacked connection" - 用
curl -v http://localhost/xxx看原始响应头,确认Content-Length是否合理、Content-Type是否正确(比如text/plain下载 zip 肯定失败) - 如果用了
Compress中间件,确保它没对二进制文件做 gzip(尤其SendFile场景),压缩会破坏文件结构
文件下载看着简单,但头和体的协作、中间件生命周期、OS 文件 I/O 特性这三层容易叠着出问题——调不通时,先关掉所有中间件,裸跑一个最小 handler 看是否正常,再逐个加回来定位。











