如何在后端渲染 HTML 并生成 PDF 文件

12次阅读

如何在后端渲染 HTML 并生成 PDF 文件

本文介绍如何在 node.js 后端通过无头 浏览器(如 puppeteer)动态渲染 html 页面并生成高质量 pdf,适用于用户定制化文档的自动化存档场景。

在构建 SaaS 或企业级应用时,常需为每位用户生成个性化文档(如合同、报告、发票),前端 用 React 动态填充数据并支持打印;但仅靠前端打印无法满足服务端自动归档、审计或异步处理的需求。此时,必须在后端完成 HTML 渲染与 PDF 生成闭环 ——而直接在服务端“运行 React”不可行(React 是客户端框架,依赖 DOM 和浏览器环境),正确路径是: 服务端提供可被浏览器访问的预渲染 HTML 接口(SSR 或静态路由),再由无头浏览器访问该 URL,截图 / 导出为 PDF

✅ 推荐方案:Puppeteer + Express 预渲染接口

首先,确保后端暴露一个能返回用户专属 HTML 的 HTTP 接口(例如 /api/docs/:id/pdf),该接口应:

  • 根据请求参数(如 userId 或 docId)查询用户数据;
  • 使用模板引擎(如 EJS、Handlebars)或纯字符串拼接生成完整 HTML(含内联 CSS,避免外部资源加载失败);
  • 返回标准 HTML 响应(Content-Type: text/html),确保样式完整、布局可控。

示例 Express 路由:

app.get('/api/docs/:id/pdf', async (req, res) => {const { id} = req.params;   const user = await db.getUser(id); // 替换为你的数据源   const html = `                                                          

用户协议

姓名:${user.name}

立即学习 前端免费学习笔记(深入)”;

邮箱:${user.email}

签署日期:${new Date().toLocaleDateString()}

`; res.send(html); });

接着,使用 Puppeteer 访问该 URL 并生成 PDF:

const puppeteer = require('puppeteer');  async function generatePdfFromUrl(url, options = {}) {const browser = await puppeteer.launch({     headless: true,     args: ['--no-sandbox', '--disable-setuid-sandbox']   });   try {const page = await browser.newPage();     // 设置视口以匹配 A4 尺寸(确保布局准确)await page.setViewport({width: 1200, height: 800});     await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000});      const pdfBuffer = await page.pdf({format: 'A4',       printBackground: true,       margin: { top: '20px', right: '20px', bottom: '20px', left: '20px'},       ……options     });      return pdfBuffer;   } finally {await browser.close();   } }  // 调用示例(如保存到文件系统)const pdf = await generatePdfFromUrl('http://localhost:3000/api/docs/123/pdf'); require('fs').writeFileSync('./output/user-123.pdf', pdf);

⚠️ 关键注意事项

  • CSS 与字体:PDF 导出不支持所有 CSS(如 flexbox 在某些版本中渲染异常),推荐使用语义化 HTML + 稳定 CSS(如 float、inline-block 或表格布局);字体需确保已加载(可内联 @font-face 或使用系统安全字体)。
  • 资源加载:避免引用外部 CDN 资源(如未代理的 Google Fonts),否则无头浏览器可能因网络策略失败;建议将字体、图片转为 base64 内联。
  • 性能与并发:Puppeteer 实例较重,生产环境务必复用浏览器实例(使用 puppeteer.launch({headless: true}) 单例 + browser.newPage() 多页复用),或采用连接池管理。
  • 安全性:切勿将用户输入直接拼入 HTML 而不做转义,防止 XSS;使用 DOMPurify.sanitize() 或模板引擎自动转义机制。
  • 替代方案权衡:若文档结构简单且无需 JavaScript 交互,可考虑 pdfmake 或 html-pdf(基于 PhantomJS,已停止维护),但 Puppeteer 是当前最稳定、兼容性最佳的选择。

通过以上方式,你既能保持前端 React 的交互体验,又能在后端可靠地生成、存储和管理用户专属 PDF 文档,真正实现前后端职责分离与能力互补。

text=ZqhQzanResources