本文介绍如何在 node.js 后端通过无头浏览器(如 puppeteer)动态渲染 html 页面并生成高质量 pdf,适用于用户定制化文档的自动化存档场景。
在构建 SaaS 或企业级应用时,常需为每位用户生成个性化文档(如合同、报告、发票),前端用 React 动态填充数据并支持打印;但仅靠前端打印无法满足服务端自动归档、审计或异步处理的需求。此时,必须在后端完成 HTML 渲染与 PDF 生成闭环——而直接在服务端“运行 React”不可行(React 是客户端框架,依赖 DOM 和浏览器环境),正确路径是:服务端提供可被浏览器访问的预渲染 HTML 接口(SSR 或静态路由),再由无头浏览器访问该 URL,截图/导出为 PDF。
首先,确保后端暴露一个能返回用户专属 HTML 的 HTTP 接口(例如 /api/docs/:id/pdf),该接口应:
示例 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);通过以上方式,你既能保持前端 React 的交互体验,又能在后端可靠地生成、存储和管理用户专属 PDF 文档,真正实现前后端职责分离与能力互补。