Go Fiber 不支持多文件打包下载,单文件用 c.SendFile() 需防路径穿越,多文件需 io.Pipe + archive/zip 流式构造并显式关闭 zw,响应头须设 Content-Type 和 url.PathEscape 编码的 Content-Disposition。

Go Fiber 没有开箱即用的“多文件打包下载”能力,单文件下载靠 c.SendFile() 或 c.Attachment() 就能搞定,但 ZIP 打包必须自己流式构造。
单文件下载:用 c.SendFile() 最简但要注意路径安全
直接传本地文件路径给 c.SendFile("path/to/file.pdf") 是最快方式,Fiber 会自动设置 Content-Type 和 Content-Disposition。但别忘了:
– 路径不能由用户输入拼接,否则可能触发目录穿越(如 ../../../etc/passwd)
– 建议白名单校验文件名后缀,或用 filepath.Base() 提取纯文件名
– 如果文件不在当前进程可读路径下(比如在 MinIO),就得用 c.Response().WriteBody() 手动写入字节流
多文件打包下载:必须用 io.Pipe + archive/zip 流式生成
Fiber 不提供批量压缩接口,c.SendFile() 只收单路径,c.Attachment() 只设头不压缩——硬塞 ZIP 字节数组会 OOM。正确做法是:
– 启一个 goroutine 往 io.Pipe 的 writer 写 ZIP 数据
– 主线程把 pipe reader 直接交给 c.Response().WriteBody()
– 每个 zip.FileHeader.Name 必须规范(如 "images/photo.jpg"),禁用 "../" 类路径
– 写完所有文件后,必须调用 zw.Close(),且之前要 defer zw.Flush(),否则解压报 “invalid zip file”
响应头和中文文件名:浏览器兼容性就卡在这两行
ZIP 下载要能点开、不乱码,这两句头缺一不可:
– c.Set("Content-Type", "application/zip")
– c.Set("Content-Disposition", `attachment; filename="`+url.PathEscape("导出文件.zip")+`"`)
注意:
– filename= 后面的值必须用 url.PathEscape() 编码,否则 IE/旧版 Safari 会乱码或截断
– ZIP 内部文件名默认 UTF-8,只要 Go 源文件是 UTF-8(现代编辑器默认),且 zip.FileHeader.NonUTF8 = false(默认值),就不会出现解压后中文变问号
大文件或高并发下载:连接管理和内存控制不能靠运气
用户中途关浏览器、Nginx 代理超时、服务端卡住——这些都会导致下载失败或卡死:
– 显式调用 c.Context().SetConnectionClose(true),避免 fasthttp 复用连接引发粘包
– ZIP 生成 goroutine 内不要用 time.Sleep 或阻塞 IO;改用 context.WithTimeout 控制总耗时
– 如果 ZIP 内含上百个大文件,考虑加限速逻辑(如每秒写入 ≤10MB),防止压垮网卡或下游代理
最容易被忽略的是:流式 ZIP 必须等 zw.Close() 完全返回,才能认为响应结束;而 Fiber 不会自动等待 goroutine。你得确保 pipe writer goroutine 执行完毕,否则客户端收到的 ZIP 总是损坏。


















