Beego 不支持 PDF 渲染,必须用 gofpdf 等库生成二进制流并直写响应体、设置 Content-Type 和 Content-Disposition 响应头;模板引擎仅输出 HTML,无法生成 PDF;中文需显式加载 TTF 字体并严格匹配字体名与路径。

Beego 本身不支持 PDF 渲染,必须在 Controller 中调用 PDF 库(如 gofpdf)生成二进制流,再通过 c.Ctx.ResponseWriter 直接输出,同时设置正确的响应头——任何试图用 c.TplName 渲染 PDF 模板的做法都会失败。
为什么不能走 Beego 模板流程导出 PDF
Beego 的模板引擎(html/template)只输出 HTML 字符串,它没有 PDF 编码能力。常见错误是把 invoice.tpl 当作 PDF 模板,调用 Parse 后直接 c.ServeJSON 或写入响应体,结果浏览器收到的是 HTML 文本,打开乱码或下载后无法打开。
-
c.TplName = "invoice.tpl"+c.ServeJSON()→ 输出 JSON 包裹的 HTML 字符串,不是 PDF -
template.ParseFiles("invoice.tpl")→ 返回[]byte是 HTML,不是 PDF 二进制流 - 未设置
Content-Type: application/pdf→ 浏览器按 text/html 解析,显示源码或报错
用 gofpdf 生成中文 PDF 的关键步骤
中文支持是高频踩坑点:默认字体不支持 UTF-8,不显式加载字体就会满屏方块。路径、函数调用顺序、字体名一致性缺一不可。
- 准备一个 TTF 中文字体文件(如
simhei.ttf),放在项目static/fonts/下 - 在初始化
pdf := gofpdf.New(...)后**立即**调用pdf.AddUTF8Font("simhei", "", "static/fonts/simhei.ttf") - 后续所有文本操作前必须调用
pdf.SetFont("simhei", "", 12);传入的字体名("simhei")必须和AddUTF8Font第一个参数完全一致 - 字体路径是相对于进程启动时的工作目录(
os.Getwd()),不是代码所在路径;建议用filepath.Join(filepath.Dir(os.Executable()), "..", "static", "fonts", "simhei.ttf")动态拼接
Controller 中导出 PDF 的最小可行结构
核心是绕过模板,纯手动构造 PDF 数据并控制响应头。以下为 gofpdf 实操要点:
- 响应头必须在任何
Write()之前设置:c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/pdf")和c.Ctx.ResponseWriter.Header().Set("Content-Disposition", "attachment; filename=\"invoice.pdf\"") - 用
pdf.Output(nil)获取[]byte,或直接用pdf.Output(c.Ctx.ResponseWriter)输出到响应体 - 不要调用
c.StopRun()以外的任何 Beego 响应方法(如c.Data["json"]、c.TplName),否则会冲突 - 若需复用 HTML 模板内容(如抬头、条款),先用
template.ExecuteToString()渲染成字符串,再用pdf.MultiCell()填充——但表格、样式、换行需手动处理,无自动转换
前端如何安全触发下载
后端返回 PDF 二进制流后,前端不能用 window.open(),否则可能被拦截或丢失文件名。推荐用 Blob + <a download>:
- 请求用
fetch(url, { responseType: 'blob' })或axios.get(url, { responseType: 'blob' }) - 创建
URL.createObjectURL(new Blob([response.data], { type: 'application/pdf' })) - 用虚拟
<a>标签绑定href和download属性后触发点击 - 注意:Safari 对
download属性支持有限,若兼容性要求高,后端可改用重定向到带签名的一次性 URL
字体加载、响应头顺序、二进制流直写——这三点漏掉任意一个,PDF 就不会按预期生成或下载。别信“模板转 PDF”这种模糊说法,Beego 里没有银弹,只有明确控制字节流的路径。



















