gofpdf动态生成pdf需直接输出到http.responsewriter,必须先注册中文字体、设置content-type/content-disposition(含utf-8编码文件名)及cache-control头,再调用pdf.output();图片须用绝对路径或bytes.buffer加载并指定readdpi:true。

直接用 c.File() 肯定不行——那是为已存在文件设计的;动态生成 PDF 必须走响应流,核心是把生成器输出写进 http.ResponseWriter,同时设对头信息。
gofpdf 输出到 ResponseWriter 而不是文件
gofpdf 默认写到 io.Writer,而 http.ResponseWriter 恰好实现了它。别调 pdf.OutputFile() 或 pdf.WriteTo() 到磁盘,直接传 c.Writer:
- 创建 PDF 实例后,立刻调用
pdf.AddFont()注册中文字体(如"simhei"),否则中文全方块 - 必须在
pdf.Output(c.Writer)前设置全部 header:Content-Type、Content-Disposition、Cache-Control -
Content-Disposition的filename*=形式才支持 UTF-8 中文名,filename="报表.pdf"在 Chrome 里会乱码 - PDF 内容生成过程若耗时较长,需提前设置
c.Writer.WriteHeader(http.StatusOK)防止超时中断
gin 路由里处理 PDF 流的典型写法
别用 c.JSON() 或 c.String(),它们会覆盖 writer 状态。完整流程是:
- 先调
c.Header("Content-Type", "application/pdf") - 再设
c.Header("Content-Disposition", "attachment; filename*=UTF-8''%E6%8A%A5%E8%A1%A8.pdf") - 加
c.Header("Cache-Control", "no-store")防 CDN 缓存旧版 - 然后 new gofpdf、AddFont、AddPage、Cell… 最后
pdf.Output(c.Writer) - 注意:一旦
pdf.Output()开始写 body,header 就不能再改
图片嵌入路径和 DPI 陷阱
PDF 里插图不能靠相对路径硬写,尤其在 Docker 容器里:./assets/chart.png 极大概率找不到。
- 图表来自 HTTP 接口?用
http.Get()下载到bytes.Buffer,再传给pdf.ImageFromBytes() - 本地图片务必用绝对路径,或基于可执行文件目录拼接:
filepath.Join(os.Getenv("PWD"), "assets", "logo.png") - PNG 图片要保留清晰度,必须显式传
gofpdf.ImageOptions{ImageType: "PNG", ReadDpi: true};不设就按 72 DPI 渲染,图表糊成一片 - gofpdf 不支持 SVG,得先转 PNG;unidoc 支持矢量但社区版带水印且页数受限
前端接收 blob 并触发下载
后端流式输出 PDF,前端必须用 responseType: "blob" 接收,不能当字符串处理:
- axios/fetch 返回的
res.data是Blob对象,不是 base64 字符串 - 别用
URL.createObjectURL(new Blob([res.data]))—— 这样多包一层,效率低还容易内存泄漏 - 直接
new Blob([res.data], {type: "application/pdf"})创建 blob,再用URL.createObjectURL() - 下载完成后必须调
window.URL.revokeObjectURL(href),否则 blob 对象长期驻留内存
最易被忽略的是 header 设置时机和字体注册顺序:gofpdf 的 AddFont() 必须在 AddPage() 之前,而所有 header 必须在 pdf.Output() 第一个字节写出前完成。这两个点一错,PDF 就打不开或中文全方块。











