您现在的位置:新闻首页>资本 > 如何将HTML转成PDF?方法介绍
如何将HTML转成PDF?方法介绍
在本文中,我将展示如何使用 Node.js、Puppeteer、headless Chrome 和 Docker 从样式复杂的 React 页面生成 PDF 文档。
相关:《nodejs 教程》
背景:几个月前,一个客户要求我们开发一个功能,用户可以得到 PDF 格式的 React 页面内容。该页面基本上是患者病例的报告和数据可视化结果,其中包含许多 SVG。另外还有一些特殊的请求来操纵布局,并对 HTML 元素进行一些重新排列。因此与原始的 React 页面相比,PDF 中应该有不同的样式和额外的内容。
由于这个任务比用简单的 CSS 规则解决要复杂得多,所以我们先探讨了可能的实现方法。我们找到了 3 个主要解决方案。这篇博文将指导你了解它们的可能性并最终实施。
目录:在客户端还是服务器端生成?方案1:从 DOM 制作屏幕截图方案2:仅使用 PDF 库
最终方案3:Node.js、Puppeteer 和 Headless Chrome
样式控制将文件发送到客户端并保存在 Docker 中使用 Puppeteer方案3 +1:CSS打印规则总结在客户端还是服务器端生成?
在客户端和服务器端都可以生成PDF文件。但是让后端处理它可能更有意义,因为你并不想耗尽用户浏览器可以提供的所有资源。
即便如此,我仍然会展示这两种方法的解决方案。
方案1:从 DOM 制作屏幕截图
乍一看,这个解决方案似乎是最简单的,事实证明的确是这样,但它有其自身的局限性。如果你没有特殊需求,例如在 PDF 中选择文本或对文本进行搜索,那么这就是一种简单易用的方法。
此方法简单明了:从页面创建屏幕截图,并把它放到 PDF 文件中。非常直截了当。我们可以使用两个包来实现:
Html2canvas,根据 DOM 生成截图jsPdf,一个生成PDF的库
开始编码:
npm install html2canvas jspdf
import html2canvas from 'html2canvas' import jsPdf from 'jspdf' function printPDF () { const domElement = document.getElementById('your-id') html2canvas(domElement, { onclone: (document) => { document.getElementById('print-button').style.visibility = 'hidden' }}) .then((canvas) => { const img = canvas.toDataURL('image/png') const pdf = new jsPdf() pdf.addImage(imgData, 'JPEG', 0, 0, width, height) pdf.save('your-filename.pdf') })
就这样!
请注意 html2canvas 的 onclone方法。当你在截图之前需要操纵 DOM(例如隐藏打印按钮)时,它是非常方便的。我看到过很多使用这个包的项目。但不幸的是,这不是我们想要的,因为我们需要在后端完成对 PDF 的创建工作。
方案2:只使用 PDF 库
NPM上有几个库,如 jsPDF(如上所述)或PDFKit。他们的问题是,如果我想使用这些库,我将不得不重新调整页面结构。这肯定会损害可维护性,因为我需要将所有后续更改应用到 PDF 模板和 React 页面中。
请看下面的代码。你需要亲自手动创建 PDF 文档。你需要遍历 DOM 并找出每个元素并将其转换为 PDF 格式,这是一项繁琐的工作。必须找到一个更简单的方法。
doc = new PDFDocument doc.pipe fs.createWriteStream('output.pdf') doc.font('fonts/PalatinoBold.ttf') .fontSize(25) .text('Some text with an embedded font!', 100, 100) doc.image('path/to/image.png', { fit: [250, 300], align: 'center', valign: 'center' }); doc.addPage() .fontSize(25) .text('Here is some vector graphics...', 100, 100) doc.end()
这段代码段来自 PDFKit 文档。但是如果你的目标是直接生成一个 PDF 文件,而不是对一个已经存在的(并且不断变化的)HTML 页面进行转换,它还是很有用的。
最终方案3:基于 Node.js 的 Puppeteer 和 Headless Chrome
什么是Puppeteer?其文档中写道:
Puppeteer 是一个 Node 库,它提供了一个高级 API 来控制 DevTools 协议上的 Chrome 或 Chromium。 Puppeteer 默认以 headless 模式运行 Chrome 或 Chromium,但其也可以被配置为完整的(non-headless)模式运行。
它本质上是一个可以从 Node.js 运行的浏览器。如果你读过它的文档,其中首先提到的就是你可以用 Puppeteer 来生成页面的截图和PDF。优秀!这正是我们想要的。
先用 npmi i puppeteer 安装 Puppeteer,并实现我们的功能。
这是一个简单的功能,可导航到 URL 并生成站点的 PD F文件。
首先,我们启动浏览器(仅在 headless 模式下支持 PDF 生成),然后打开新页面,设置视口,并导航到提供的URL。
设置 waitUntil:'networkidle0' 选项意味着当至少500毫秒没有网络连接时,Puppeteer 会认为导航已完成。 (可以从API docs 获取更多信息。)
之后,我们将 PDF 保存为变量,关闭浏览器并返回 PDF。
注意:page.pdf 方法接收 options 对象,你可以使用 'path' 选项将文件保存到磁盘。如果未提供路径,则 PDF 将不会被保存到磁盘,而是会得到缓冲区。(稍后我将讨论如何处理它。)
如果需要先登录才能从受保护的页面生成 PDF,首先你要导航到登录页面,检查表单元素的 ID 或名称,填写它们,然后提交表单:
await page.type('#email', process.env.PDF_USER) await page.type('#password', process.env.PDF_PASSWORD) await page.click('#submit')
要始终将登录凭据保存在环境变量中,不要硬编码!
样式控制
Puppeteer 也有这种样式操作的解决方案。你可以在生成 PDF 之前插入样式标记,Puppeteer 将生成具有已修改样式的文件。
await page.addStyleTag({ content: '.nav { display: none} .navbar { border: 0px} #print-button {display: none}' })将文件发送到客户端并保存
好的,现在你已经在后端生成了一个 PDF 文件。接下来做什么?
如上所述,如果你不把文件保存到磁盘,将会得到一个缓冲区。你只需要把含有适当内容类型的缓冲区发送到前端即可。
printPDF.then(pdf => { res.set({ 'Content-Type': 'application/pdf', 'Content-Length': pdf.length }) res.send(pdf)
现在,你只需在浏览器向服务器发送请求即可得到生成的 PDF。
function getPDF() { return axios.get(`${API_URL}/your-pdf-endpoint`, { responseType: 'arraybuffer', headers: { 'Accept': 'application/pdf' } })
一旦发送了请求,缓冲区的内容就应该开始下载了。最后一步是将缓冲区数据转换为 PDF 文件。
savePDF = () => { this.openModal(‘Loading…’) // open modal return getPDF() // API call .then((response) => { const blob = new Blob([response.data], {type: 'application/pdf'}) const link = document.createElement('a') link.href = window.URL.createObjectURL(blob) link.download = `your-file-name.pdf` link.click() this.closeModal() // close modal }) .catch(err => /** error handling **/) } <button onClick={this.savePDF}>Save as PDF</button>
就这样!如果单击“保存”按钮,那么浏览器将会保存 PDF。
在 Docker 中使用 Puppeteer
我认为这是实施中最棘手的部分 —— 所以让我帮你节省几个小时的百度时间。
官方文档指出“在 Docker 中使用 headless Chrome 并使其运行起来可能会非常棘手”。官方文档有疑难解答部分,你可以找到有关用 Docker 安装 puppeteer 的所有必要信息。
如果你在 Alpine 镜像上安装 Puppeteer,请确保在看到页面的这一部分时再向下滚动一点。否则你可能会忽略一个事实:你无法运行最新的 Puppeteer 版本,并且你还需要用一个标记禁用 shm :
否则,Puppeteer 子进程可能会在正常启动之前耗尽内存。
方案 3 + 1:CSS 打印规则
可能有人认为从开发人员的角度来看,简单地使用 CSS 打印规则很容易。没有 NPM 模块,只有纯 CSS。但是在跨浏览器兼容性方面,它的表现如何呢?
在选择 CSS 打印规则时,你必须在每个浏览器中测试结果,以确保它提供的布局是相同的,并且它不是100%能做到这一点。
例如,在给定元素后面插入一个 break-after 并不是一个多么高深的技术,但是你可能会惊讶的发现要在 Firefox 中使用它需要使用变通方法。
除非你是一位经验丰富的 CSS 大师,在创建可打印页面方面有很多的经验,否则这可能会非常耗时。
如果你可以使打印样式表保持简单,打印规则是很好用的。
让我们来看一个例子吧。
上面的 CSS 隐藏了打印按钮,并在每个 div 之后插入一个分页符,其中包含content 类。有一篇很棒的文章总结了你可以用打印规则做什么,以及它们有什么问题,包括浏览器兼容性。
考虑到所有因素,如果你想从不那么复杂的页面生成 PDF,CSS打印规则非常有效。
总结
让我们快速回顾前面介绍的方案,以便从 HTML 页面生成 PDF 文件:
从 DOM 产生截图:当你需要从页面创建快照时(例如创建缩略图)可能很有用,但是当你需要处理大量数据时就会有些捉襟见肘。只用 PDF 库:如果你打算从头开始以编程方式创建 PDF 文件,这是一个完美的解决方案。否则,你需要同时维护 HTML 和 PDF 模板,这绝对是一个禁忌。Puppeteer:尽管在 Docker 上工作相对困难,但它为我们的实现提供了最好的结果,而且编写代码也是最简单的。CSS打印规则:如果你的用户受过足够的教育,知道如何把页面内容打印到文件,并且你的页面相对简单,那么它可能是最轻松的解决方案。正如你在我们的案例中所看到的,事实并非如此。
打印快乐!
英文原文地址:
编程入门!!
下一篇:图灵机计算模型的主要贡献是什么
-
经济 业界 推荐 美圆指数 29美元 福汇外汇 港币兑换美元 公信宝 币世界 ok币 加拿大元汇率 金条价格走势 ok交易所 白银套利 ppi指数 金价走势分析 中币交易所 玩客币行情 港币兑美元 马来西亚货币 今日复明日 旧日噩梦 bullish 海曼明斯基 绿天鹅 黄金行情走势 汇率日元 火币pro 莱茨狗 fx57 美元价格 币世界快讯 金价格走势图 隔夜利率 全球货币战争 波场tron 2199美元 stdaily 伊朗油价 国际石油行情 btcchina 美元日元汇率 恒生指数实时 大立光股票 回升 hc币 夏盈盈 希腊公投 市场黄金价格 黄金k线走势图 蜡烛图 单均线交易 日元美元 国际油价趋势 比特币白皮书 2012年金价走势 usdt 白银价钱 今日石油价格 fx1800 缩表 油价走势 台股 sdag 杨林科 港币汇率 明斯基时刻 猛烈打压 stellar 隔夜美股行情 白银行情 dp1s 油价 微比特 meiyuan 香港恒生指数 成交量分析 白银比例 实时行情 白银 国际石油 ltc是什么币种 美元指数走势 期货实时行情 美元兑澳元 中期选举 美元指数dini rsi指标 美金兑港币 谦益农业 硬币回收价表 今天美元走势 太一云 间谍车 加元汇率 国际石油价格 意大利国债 澳元走势预测 btc挖矿 美原油行情 即时外汇 制造业指数 澳元汇率 美国股市休市 下周美元走势 欧债 玩客云 美原油连 道琼指数 币种 美元汇率走势 文章档案 外汇止损多少 以太 挖矿 vshen 极路由hiwifi 汇丰pmi adx 美元兑日元 全球央行年会 btm 空投 安币交易所 chaobi otc交易平台 金价 标普500期货 加币汇率走势 日元兑换美元 伦敦铜价 著名财经 国际油价查询 etc 外汇学习 美债收益率 阿希币 pEE币 什么是头寸 纽交所 钻石底 德国30 799澳元 持仓报告 玩客 原油走势图 港股恒生指数 欧元下跌 420欧元 金子价格 加元走势图 1.11111E+11 xrp 美元指数k线图 金价走势预测 最新黄金价格 铜价格走势图 黄金降价 汇率欧元 金针探底 原油成本 美元 strllar 泰奇猫 圈牌 金价走势 以太币 lme铜实时行情 eos价格走势 欧元兑美金 外汇基本知识 联邦基金利率 伦敦银走势图 基本面分析 空头回补 云鱼 py6是什么货币 rsi指标详解 265万澳元 国际油价格 gateio wti原油走势图 门罗币 白银价格走势 欧盟财长会议 外汇咨询 交叉盘 外汇初学 房价指数 cbt 比特股 ltc 隐私政策 石油危机 日圆汇率 英国股市指数 原油最新价格 行情报价 自动减支 黄金市场价 全球指数 imtoken 币投资 10美金 eos币价格 相对强弱指标 黄金年走势图 美原油 加元美元 虚拟币 值多少钱 国际油价 外汇哈里森 外汇交易分析 白银价格分析 日bi btcc 标准普尔500 wti原油价格 zbcom 和币 度宇宙 技术指标分析 全球股市指数 币久 白银价格趋势 克龙 银行回收硬币 hiwifi 贝尔链 美元兑换欧元 后座议员 黄金市场行情 德拉基讲话 UES 道琼斯k线图 美元对日元 k线图分析 恒生指数 英国脱欧时间 港股指数 比特币之父 bin 今日原油 jinjia 日经225指数 比特币价格 英镑汇率 742 大立光 外汇走势 上吊线 趣步APP被调查 肖野 理财三 铜走势图 艾达 吞阳 coinex 欧元美金 赵长鹏 法郎汇率 9g游戏 英国脱欧结果 硅谷bbs 俄罗斯火星人 铜价 什么叫头寸
-
新罪证 苹果 克林顿 行业 S400 企业 违法 核实 420欧元 专家 朋友 协议 各不相同 5 法院 补贴 太空 在朝 对方 还在 2018 放了 白银价格分析 币久 驾校 铺装 问题 心脏 房屋 制服 此前 eth 独股一箭 三大评级机构 视频 交警 夏天 尽在 淑女的品格 受审 加征 延续 achain 联合国 他人 82岁 后边 方法 东部 强者 50个基点是多少 挖矿 北约 闪婚 wifi 滴滴 藏区 灵魂 精神 8页 实物 回升 美原油走势 外汇初学 纽交所 itc wti原油走势图 微博 营业 七旬 皇马 身 人用 开锣 张勇 沈阳 防弹 7500元 地震 明细 黄金价格分析 油价行情 外汇报价 真 开出 就是 百搭 了解 一下 东来 号的 Xperia 合理 铺地板 是个 盟友 军事化 好不好 福汇外汇 满楼水平 澳大利亚大选 港股恒生指数 gotowebinar 台股 chaobi 玩客 旧日噩梦 油船 核工业 写下 成鬼 扔书 砸伤 大学生 中学 主任 家装 首登 阅兵 业界 adx 6000澳元 失业率 期货实时行情 瑞郎 日币汇率走势 铜价格走势图 原油最新价格 vshen xrp usdt 硬币回收价表 币世界 pai币 riyuan 下水 欢迎 男子 不能 一层 衣服 品牌 提高 三星 蚂蚁 经验 美国总统 不断 加油站 20倍 行情报价 欧债危机 黄金价格表 huobi explore2fs nexus10 太极链 相对强弱指标 理财三 标准普尔500 白银价格走势 艾达 otc交易平台 波场tron 联署 罪证 国防部 家长 千元 30岁 24人 抓住 小物 联手 fashion 解 Note9 新增 大教堂 人社部 遭遇 好酒 3000 一开 风口 携旱 传奇 黄金行情走势 sosobtc 2599澳元 瑞典货币 台湾一周重点 财经日历 道琼斯指数 大立光 肖野 欧元下跌 btc挖矿 ok币 封信 3号 吊销 巴拿马 完全 扩大 获赔 传销 打印机 校园 种藕 帮你 太太 主题 顾客 vivo 限制 3.2亿 化工 发言人 陈宁 赛场 打我 献给 连接 海域 无线 非官方 班主任 举措 23日 鏖战 Comic 白银 金价走势分析 美国指数 国际石油行情 日经 德国新闻中文 交易侠 德拉吉 俄罗斯物价 今日恒生指数 k线图解读 pee 新加坡元汇率 墨西哥汇率 2019通货膨胀率 什么叫头寸 趣步APP被调查 文章档案 外汇学习 玩客币行情 国际石油价格 公信宝 沙特 地球 直播 总书记 科技 税延型 适合 工作人员 当天 海军 高层 彻底 弃核 印媒
-
2599澳元 微博 装甲车 今日原油 藏区 明细 行业 420欧元 协议 新罪证 地震 放了 人心 家装 首登 什么是头寸 沦丧 对方 联手 什么 福汇外汇 中国佬汉堡 房屋 克林顿 加征 违法 8l9970 3号 S400 2018 苹果 这条 8寸 油价 鲜 平美 业界 cbt 欢迎 法院 企业 日本 金价走势分析 ok交易所 stellar 罪证 心痛 姐姐 失业率 国际石油行情 公信宝 513部队 打我 受审 核实 问题 白银价格分析 日经225指数 外汇交易分析 台股 联合国 专家 防空导弹 gaga 2300X 回升 北约 扔书 各不相同 宽松 5 家乡 黄金行情走势 港币兑换美元 今日恒生指数 hc币 旧日噩梦 币世界 首尔至平壤 太空 太太 他见 淑女的品格 大全 白银套利 海曼明斯基 fx 币久 国际石油价格 全球货币战争 钻石底 课堂 在朝 彻底 今夏 号的 制服 8页 仍未 防弹 军事化 绿天鹅 金价格走势图 蜡烛图 度宇宙 玩客币行情 币世界快讯 中币交易所 usdt 大爷 驾校 还在 补贴 品牌 低帮版 连接 好不好 纽交所 白银行情 白银价钱 早晨之星 平均线 achain 油价行情 银价走势图 太极链 美元指数k线图 加币汇率走势 美元日元汇率 间谍车 硬币回收价表 多好 30岁 心脏 以为 帮你 3.2亿 灵魂 抗诉 猛烈打压 rsi指标 fx1800 eth 独股一箭 伊朗油价 bullish 黄金价格表 三大评级机构 wti原油走势图 日bi k线图分析 缩表 油价走势 玩客 国际油价趋势 2012年金价走势 联署 强征 世界杯 完全 视频 交警 夏天 尽在 三星 化工 装修 德国 此前 刘强 一笔 白银 2199美元 黄金k线走势图 外汇初学 美原油行情 6000澳元 加拿大元汇率 美国指数 期货实时行情 日经 itc bitebi 上吊线 全球股市指数 和币 港股恒生指数 铜价格走势图 值多少钱 日圆汇率 ltc是什么币种 美元指数走势 美债收益率 挖矿 btcchina 千元 传销 wifi 小物 方法 滴滴 烘焙 fashion Xperia 质量 铺装 沈阳 一字 强者 外汇哈里森 市场黄金价格 单均线交易 希腊公投 瑞郎 币投资 趣步APP被调查 美元对日元 全球指数 比特币价格 中期选举 最新黄金价格 澳元走势预测 夏盈盈 火币pro 拍卖 凌晨 侧脸 伤残 熬夜 后边 还能 一层 张勇 21600 主任 陈宁 精神 献给 东部 加油站 脱贫 20倍 stdaily 白银价格趋势 今日石油价格 汇率日元 隔夜利率 著名财经 黄金价格分析 外汇走势 265万澳元 比特股 港股指数 外汇咨询 英国脱欧时间 银行回收硬币 原油最新价格 克龙 比特币白皮书 vshen 成交量分析 玩客云 当天 真 获赔 他人 上门 人用







