后端Nodejs + 前端Vue 实现 HTML 转 PDF 并导出(方案二:puppeteer nodejs express 实现)
后端Nodejs + 前端Vue 实现 HTML 转 PDF 并导出(方案二:puppeteer nodejs express 实现)
·
近期公司提出了一个新需求,希望将用户在前端填写的一系列数据生成一个报告给用户,报告大概有8个表格,表格涉及到分页。于是查询了资料,做出以下两个方案。
方案一请点击这里
方案二
puppeteer
生成页面的截图和PDF
Nodejs使用
使用express框架搭建简单的node服务,并且安装puppeteer、pdf-lib
- 安装
npm install --save puppeteer
npm install --save pdf-lib
- 相关代码
直接调用node服务接口进行pdf的生成
http://localhost:3000/report?reportId=test&dirName=test&reportName=test
const express = require('express');
const router = express.Router();
const puppeteer = require('puppeteer');
const fs = require('fs');
const { v4: uuidV4 } = require('uuid');
const { PDFDocument } = require('pdf-lib');
router.get('/', async (req, res, next) => {
const { reportId, dirName = '', reportName } = req.query || {};
if (!reportId) {
res.send({
code: 40000,
msg: "请输入reportId"
});
return;
}
// const folder = `/app/pdfReport/${dirName}`; // 部署使用
const folder = `d:/report/${dirName}`; // 本地测试使用
fs.access(folder, err => {
if (err) {
fs.mkdir(folder, { recursive: true }, err => {
if (err) throw err;
});
} else {
console.log('dir exists');
}
});
try {
// 启动无头浏览器
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
headless: true,
});
const defaultConfig = {
format: 'A4',
printBackground: true,
margin: {
top: '2cm',
right: '2cm',
bottom: '2cm',
left: '2cm',
},
};
// 封面
const reportCover = await browser.newPage();
// 不再有网络连接时触发
await reportCover.goto(`http://127.0.0.1:8080/report-cover?reportName=${reportName}`, { waitUntil: 'networkidle0' });
const reportCoverBuffer = await reportCover.pdf({
...defaultConfig,
displayHeaderFooter: false,
});
// 关闭页面
reportCover.close();
// 内容
const reportContent = await browser.newPage();
// 不再有网络连接时触发
await reportContent.goto(`http://127.0.0.1:8080?reportId=${reportId}`, { waitUntil: 'networkidle0' });
const pdfConfig = {
...defaultConfig,
displayHeaderFooter: true,
headerTemplate: `<div></div>`,
footerTemplate: '<div style="width:100%;text-align:right;margin-right: 20px;font-size:10px"><span class="pageNumber"></span></div>',
};
const reportContentBuffer = await reportContent.pdf({ ...pdfConfig });
// 关闭页面
reportContent.close();
// 关闭 chromium
browser.close();
// 合并pdf
const pdfDoc = await PDFDocument.create();
const coverDoc = await PDFDocument.load(reportCoverBuffer);
const [coverPage] = await pdfDoc.copyPages(coverDoc, [0]);
pdfDoc.addPage(coverPage);
const reportDoc = await PDFDocument.load(reportContentBuffer);
const reportPages = await pdfDoc.copyPages(reportDoc, reportDoc.getPageIndices());
reportPages.forEach((page) => {
pdfDoc.addPage(page);
});
const pdfBytes = await pdfDoc.save();
fs.writeFileSync(`${folder}/${reportName || reportId || uuidV4()}.pdf`, pdfBytes);
res.set({
'Content-Type': 'application/pdf',
});
res.send(Buffer.from(pdfBytes));
} catch (error) {
res.send({
code: 40001,
msg: '页面渲染出错啦',
});
}
// res.send({
// code: 200,
// });
});
module.exports = router;
缺点
- 如果页面上有错误,难于进行排查
- 需要单独一个服务
优点
- 生成pdf的速度较快
- pdf文件可进行复制
由于代码量比较大,正文中只贴了关键代码,有兴趣的可以去这个代码仓库查看
更多推荐
已为社区贡献7条内容
所有评论(0)