beego 无法直接渲染 pdf,必须在 controller 中调用 pdf 库(如 gofpdf)生成二进制流,设置 content-type: application/pdf 和 content-disposition 响应头后,通过 c.ctx.responsewriter 直接输出,绕过模板渲染流程。

Beego 中无法直接渲染 PDF,必须走后端生成再响应
Beego 自身不提供 PDF 渲染能力,它的模板引擎(html/template)只输出 HTML 字符串。想生成 PDF,必须在 Controller 里调用 PDF 库完成二进制流构造,再通过 c.Ctx.ResponseWriter 直接写入响应体——不能靠 c.TplName 走视图渲染流程。
常见错误是试图把 invoice.tpl 当成 PDF 模板直接 Parse 后“导出”,结果得到的是 HTML 文本而非 PDF 文件,浏览器会乱码或下载失败。
- 正确路径:Controller 接收参数 → 构造数据结构 → 调用 PDF 库(如
gofpdf或pdf-lib的 Go 封装)→ 生成[]byte→ 设置Content-Type: application/pdf→Write()输出 - 不要在 Beego 模板中写
{{.PDFData}}这类逻辑,它没意义 - 若需复用 HTML 模板内容,可先用
template.ExecuteToString()渲染为字符串,再传给 PDF 库做文本填充(但表格/样式需手动映射,非自动转换)
中文支持必须显式加载字体,否则全是方块
用 gofpdf 时,默认不带中文字体,SetFont("Arial") 在中文环境必然失效;pdf-lib 的 Go 版本(如 unidoc)也需提前注册字体文件。iText7 是 Java 生态的,和 Beego 无直接集成可能。
以 gofpdf 为例,必须:
- 准备一个 TTF 中文字体文件(如
simhei.ttf),放在项目static/fonts/下 - 在初始化 PDF 实例后立即调用
pdf.AddUTF8Font("simhei", "", "static/fonts/simhei.ttf") - 后续所有文本操作必须用
pdf.SetFont("simhei", "", 12),不能混用英文名 - 注意:字体文件路径是相对于执行命令的工作目录,不是代码文件位置;建议用
os.Executable()+filepath.Dir()动态拼路径
Beego Controller 中生成 PDF 的典型结构
核心是绕过模板渲染,把 PDF 生成逻辑写死在 Action 方法里,并控制 HTTP 响应头。以下为最小可行示例(使用 gofpdf):
func (c *ReportController) GetPDF() {
// 1. 获取参数(如订单ID)
orderID := c.GetString("id")
if orderID == "" {
c.Abort("400")
return
}
// 2. 查询数据(调用 model)
data := models.GetInvoiceData(orderID)
// 3. 创建 PDF 实例
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddUTF8Font("simhei", "", "static/fonts/simhei.ttf")
pdf.AddPage()
// 4. 填充内容(标题、表格等)
pdf.SetFont("simhei", "B", 16)
pdf.CellFormat(190, 10, "电子发票", "0", 1, "C", false, 0, "")
pdf.SetFont("simhei", "", 12)
pdf.CellFormat(190, 8, "订单号:"+data.Number, "0", 1, "L", false, 0, "")
// 表格循环略……
// 5. 输出到响应
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/pdf")
c.Ctx.ResponseWriter.Header().Set("Content-Disposition", `attachment; filename="invoice.pdf"`)
pdf.Output(c.Ctx.ResponseWriter)
}
关键点:pdf.Output(c.Ctx.ResponseWriter) 直接写响应流,不经过 Beego 模板层;Content-Disposition 决定是内联预览还是强制下载。
性能与并发要注意内存和临时文件泄漏
PDF 生成是 CPU+内存密集型操作,尤其含图片或大量表格时。Beego 默认每个请求起 goroutine,若未控制资源,高并发下易 OOM。
- 避免在 PDF 生成中打开未关闭的文件句柄(如用
os.Open加载字体却忘记Close) -
gofpdf的Output()方法内部会调用WriteTo(),无需额外 buffer;但若手动用OutputBytes(),务必检查返回的[]byte是否被及时 GC - 不要在循环中反复
New()PDF 实例而不释放——每个实例持有页面缓存、字体缓存等 - 生产环境建议加超时控制:
c.Ctx.Input.SetData("timeout", 30*time.Second)并在生成前校验
最易被忽略的是字体缓存:AddUTF8Font() 每次调用都会注册新字体别名,若动态传入字体路径且未去重,会导致内存持续增长。应确保该操作只执行一次(如在 init() 函数中)。











