根本原因是HTTP响应头未正确设置:Content-Type必须为application/pdf,Content-Disposition建议设为attachment; filename="xxx.pdf",且必须用c.Data()发送二进制数据,禁用c.String()等文本方法。

用Gin直接写入PDF文件时,为什么浏览器总提示“无法打开”?
根本原因是HTTP响应头没设对,不是库选得不对。Gin本身不生成PDF,它只负责把PDF二进制数据发出去,但浏览器要靠Content-Type和Content-Disposition才知道该渲染还是下载。
-
Content-Type必须设为application/pdf,不能用text/plain或留空 -
Content-Disposition推荐设为attachment; filename="report.pdf",否则Chrome可能尝试内嵌渲染失败 - 生成PDF的库(如
gofpdf或unidoc)输出的是[]byte,别误用c.String()——那是给文本用的,要用c.Data()
示例片段:
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.SetFont("Arial", "", 12)
pdf.Cell(40, 10, "Hello PDF")
buf := new(bytes.Buffer)
pdf.Output(buf)
c.Header().Set("Content-Type", "application/pdf")
c.Header().Set("Content-Disposition", "attachment; filename=report.pdf")
c.Data(200, "application/pdf", buf.Bytes())Gin中返回Excel文件,为什么用excelize生成后打不开?
常见错误是没设置正确的MIME类型,或者用c.JSON()硬塞二进制数据——Excel文件不是JSON,这么干会破坏文件头。
- Excel文件必须用
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet(.xlsx)或application/vnd.ms-excel(.xls) -
excelize生成后调f.WriteTo()到bytes.Buffer,再用c.Data()返回,别调f.SaveAs()写磁盘再读——多此一举且易出权限/路径问题 - 如果前端是axios,记得加
responseType: 'blob',否则JS会把二进制当字符串解析
关键代码段:
立即学习“go语言免费学习笔记(深入)”;
f := excelize.NewFile()
f.SetCellValue("Sheet1", "A1", "Report Date")
buf := new(bytes.Buffer)
f.WriteTo(buf) // 直接写入内存
c.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
c.Header().Set("Content-Disposition", "attachment; filename=report.xlsx")
c.Data(200, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", buf.Bytes())PDF和Excel都用bytes.Buffer,但并发高时内存暴涨怎么办?
因为每个请求都在内存里完整构造整个文件,10MB报表 × 100并发 = 1GB内存瞬时占用。这不是Gin的问题,是生成逻辑没流式处理。
- PDF:用
gofpdf不支持流式写入;改用unidoc可配合io.Pipe()边生成边写响应,但需手动管理goroutine生命周期 - Excel:excelize从v2.4.0起支持
f.WriteTo(c.Writer)——直接写HTTP响应体,不缓存全量字节,大幅降低内存压力 - 务必设
c.Writer.Size()检查是否已写入(比如用户中途关闭连接),避免panic
推荐做法:
f := excelize.NewFile()
f.SetCellValue("Sheet1", "A1", "Streaming Report")
c.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
c.Header().Set("Content-Disposition", "attachment; filename=stream.xlsx")
if err := f.WriteTo(c.Writer); err != nil {
// 连接断开时WriteTo会返回io.ErrClosedPipe,忽略即可
}本地能生成PDF/Excel,部署到Linux服务器就报错
两个高频原因:字体缺失(PDF)和临时目录不可写(Excel)。
-
gofpdf默认用Arial字体,但Alpine或精简Linux镜像没装ttf字体包,要么挂载字体文件并调pdf.AddFont(),要么改用无依赖的unidoc(商用需授权) -
excelize在生成含图片或复杂样式时,内部会创建临时文件,默认用/tmp;Docker容器若挂载了只读/tmp或没权限,就会open /tmp/xxx: permission denied - 解决办法:启动前设环境变量
EXCELIZE_TMP_DIR=/app/tmp,并确保该目录存在且可写
检查命令:
ls -l /tmp # 看权限 env | grep EXCELIZE # 看环境变量是否生效
Gin不处理文件生成逻辑,只做响应投递。真正卡点永远在:HTTP头对不对、二进制流有没有被中间件截断、生成库的依赖是否在目标环境就绪。


















