Docker 平台 NodeJs Puppeteer实现html转pdf
1. 背景
PDM系统中有需求工艺单需要打印成PDF(客户对细节要求极高),当时出了5个方案:
- 从DOM制作屏幕截图 html2canvas jspdf 文字无法拷贝
- 使用PDF库 jsPDF 或 PDFKit 按组件一个一个拼凑,不合适已有html的打印模板方式
- CSS打印规则,调用浏览器打印,pdf 文件导入到系统,操作繁琐
- itext (目前用的) 和spire功能强大,但商业用途需收费,wkhtmltopdf开源免费,后端生成,黑匣子,后端开发前端代码,可视化麻烦通过模板生成PDF的实际效果和模板效果相差较大,修改起来不好把握
- 基于Node.js的Puppeteer和HeadlessChrome 展示效果与实际生成的PDF接近
使用方案5是可以借助puppeteer调用headless浏览器生成PDF,对css的支持度很高,PDF能很大限度反应模板的样式(对css支持度很高),前端对模板的控制度更高,支持dom操作,以下是模板样式和PDF样式对比:
生成的pdf文件样式和编写的html模板样式一致度较高,可以让前端编写模板文件实时查看html内容样式,改完之后用该模板调用后端的node服务生成的pdf模板能较高程度的还原模板内容,前端可以通过模板引擎如EJS(类似于当前方案后端技术的freemarker模板)生成模板执行一些复杂的dom操作和样式控制。
2. 所需依赖
npm install express jsdom puppeteer
express: 用来和业务系统通信,返回pdf流给业务系统
jsdom: 解析DOM,在生成pdf的时候要让前端可以自定义pdf的页头页尾
puppeteer: 调用headless生成PDF
3. 核心代码
genPdfFile.js
const puppeteer = require('puppeteer');
const fs = require('fs');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
let browserInstance;
async function getBrowserInstance() {
if (!browserInstance) {
browserInstance = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
}
return browserInstance;
}
async function generatePDFFromHTML(htmlString) {
try {
// 启动Puppeteer
const browser = await getBrowserInstance();
const page = await browser.newPage();
// 页眉
let headerTemplate = "";
// 页脚
let footerTemplate = "";
{
// 构建页眉
console.log("headerTemplate");
let dom = new JSDOM(htmlString);
let document = dom.window.document;
const elementsToRemove = document.querySelectorAll(".page_start");
if(elementsToRemove.length > 0) {
headerTemplate = elementsToRemove[0].outerHTML;
}
elementsToRemove.forEach(el => el.parentNode.removeChild(el));
updatedHtmlString = dom.serialize();
}
{
// 构建页脚
console.log("footerTemplate");
dom = new JSDOM(updatedHtmlString);
document = dom.window.document;
const endelementsToRemove = document.querySelectorAll(".page_end");
if(endelementsToRemove.length > 0) {
footerTemplate = endelementsToRemove[0].outerHTML;
}
endelementsToRemove.forEach(el => el.parentNode.removeChild(el));
updatedHtmlString = dom.serialize();
}
// 设置HTML内容并生成PDF的Buffer
await page.setContent(updatedHtmlString);
// 设置 PDF 选项
const pdfOptions = {
path: 'example.pdf',
format: 'a4',
displayHeaderFooter: true,
headerTemplate,
footerTemplate,
margin: {
top: '60px',
bottom: '20px',
left: '20px',
right: '50px'
},
};
// 生成 PDF
// 如果要生成带着 screen media的pdf,在page.pdf() 前面先调用 page.emulateMedia('screen')
const pdfBuffer = await page.pdf(pdfOptions);
return pdfBuffer;
} catch(error) {
console.log(error);
}
}
module.exports = {
getBrowserInstance,
generatePDFFromHTML
}
4. DockerFile
FROM ghcr.io/puppeteer/puppeteer:latest
MAINTAINER weiqlog@126.com
USER root
RUN mkdir -p /pdfG
WORKDIR /pdfG
COPY package*.json ./
COPY . .
RUN npm instal
EXPOSE 10030
ENTRYPOINT ["node", "main.js", "10030"]
镜像地址:https://hub.docker.com/r/1505774577/html_to_pdf
使用:
docker pull 1505774577/html_to_pdf:1.0.0.dev
docker run -d -p 10030:10030 --cap-add=SYS_ADMIN 1505774577/html_to_pdf:1.0.0.dev
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。