ctx.Attachment()是Fiber中触发浏览器下载的首选方式,自动设Content-Disposition和推导Content-Type,但仅支持绝对路径本地文件,不支持字节流;中文名默认RFC 5987编码,大文件慎用,内存文件需改用SendStream配合手动设头。

用 ctx.Attachment() 发送本地文件最简单
这是 Fiber 中触发浏览器下载对话框的首选方式,它自动设置 Content-Disposition: attachment 和推导 Content-Type,但只接受本地文件路径(string),不支持字节流或 io.Reader。
- 必须传绝对路径,相对路径容易因工作目录不确定而失败;推荐用
filepath.Abs()或硬编码根路径,例如"/var/www/files/report.pdf" - 确保文件存在且 Web 进程有读权限,否则返回 500,错误日志里通常只有
"open xxx: no such file or directory" - 中文文件名默认按 RFC 5987 编码(
filename*=UTF-8''...),现代浏览器兼容良好;旧版 IE 可能乱码,需手动覆盖头:ctx.Set("Content-Disposition", `attachment; filename*=UTF-8''` + url.PathEscape(name)) - 大文件(>100MB)慎用,它底层调用
http.ServeFile,不支持断点续传,也不走SendFile零拷贝优化
内存生成文件(如 Excel/PDF)得用 ctx.SendStream()
不能把 []byte 或 *bytes.Buffer 直接喂给 ctx.Attachment(),必须改用 ctx.SendStream() 配合手动设响应头。
- 三步不能错顺序:先
ctx.Set("Content-Type", "..."),再ctx.Set("Content-Disposition", `attachment; filename="xxx.xlsx"`),最后ctx.SendStream(reader) -
SendStream()不校验数据长度,若io.Reader提前 EOF(比如 buffer 被重复读、excelize 写入未完成),浏览器会提示“文件损坏” - 避免和
ctx.JSON()混用——HTTP 响应体只能有一种类型,一旦调了SendStream()就不能再调其他写体方法
大文件流式传输要绕过 Attachment() 直接操作底层 ResponseWriter
当文件太大、需控制读取节奏、或要加鉴权/限速/日志时,就得放弃封装方法,自己用 io.Copy() 推流。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 先打开文件并检查错误,失败时立刻返回 404 或 500,避免后续无法修改状态码
- 手动设头:
ctx.Set("Content-Type", "application/pdf")、ctx.Set("Content-Disposition", `attachment; filename="large.pdf"`) - 调
ctx.Response().WriteHeader(200)(必须在任何写操作前,且只能一次) - 用
io.Copy(ctx.Response(), file)转发,结束后记得file.Close();别用ctx.Response().Write()读全再写,会吃光内存
路径遍历和安全校验不能省
Fiber 不做路径白名单校验,ctx.Attachment() 传什么路径就去读什么路径,用户构造 ../../etc/passwd 会直接泄露系统文件。
立即学习“go语言免费学习笔记(深入)”;
- 永远对输入路径调
filepath.Clean(),再用strings.HasPrefix()限制在允许目录内,例如只允许/data/downloads/下的文件 - 不要信任前端传来的原始文件名,提取后缀做白名单校验(如只允
.pdf、.xlsx),避免执行任意文件 - 如果路径来自数据库或配置,也要走一遍 clean + prefix 检查——攻击面不止是 URL 参数

















