您现在的位置:新闻首页>资本 > 如何将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 俄罗斯火星人 铜价 什么叫头寸
-
外汇交易头寸 关于黄金交易 原油wti 加拿大元汇率 印度卢比 包括 bullish 黄金市场行情 btcc 1199元 瑞典克朗 离岸美元 账户银走势图 2020年金价预测 比特币白皮书 道琼指数 美元价格 2012年金价走势 ism制造业指数 以色列 仅数 白银价格 今天原油行情 fx 环球股指汇总 火币比特币 eos币价格 持仓报告 欧元兑美金 英国股市指数 油价走势 慈善 体育用品 著名财经 财经数据 黄金白银价格 币投资 杨林科 ok交易所 513部队 装修材料 信 清单 着力点 查询 恒丰 业界 自动减支 ppi指数 欧盟财长会议 房价指数 什么是头寸 白银新闻 菲律宾比索 白银价格预测 纳斯达克100 链克行情 赵长鹏 趣步APP被调查 俄罗斯火星人 9g游戏 英国脱欧结果 日bi 10美金 门罗币 中期选举 最新黄金价格 美元日元汇率 胳膊 写入 顺序 放了 今天美元走势 白银比例 当前黄金价格 黄金价格实时 okex q币交易平台 德国股票指数 美国债务违约 即时外汇牌价 w底形态 铜价 742 什么叫头寸 欧洲峰会 肖野 欧元美金 黄金市场价 imtoken 铜走势图 黄金降价 港股恒生指数 欧元下跌 圈牌 外汇交易分析 hiwifi 美原油连 制造业指数 虚拟币 币种 国际油价格 py6是什么货币 德拉基讲话 国际石油价格 全球货币战争 香港恒生指数 玩客云 军事 微博 美防 靓号 钱却 救援 了解 地方 代人 发言人 马可波罗 匹克 酷云 印尼盾汇率 汇率日元 黄金行情分析 黄金价格行情 怎么买比特币 港币兑换美元 澳币 英国公投脱欧 台湾一周重点 白银价格分析 安币 coinex eos价格走势 道琼斯k线图 相对强弱指标 加拿大就业 恒生指数 交叉盘 美元兑日元 全球央行年会 美原油 成交量分析 云鱼 澳元走势预测 sdag 以太 挖矿 安币交易所 btcchina 明星 副市长 2018 新增 献金 两车 来了 导致 九段 经济 下周美元走势 蜡烛图 2599澳元 金克拉泽 gasstation 今日油价走势 恒指模拟交易 道琼斯指数 大立光 法郎汇率 隐私政策 美元对日元 原油成本 虚拟货币开发 理财三 硅谷bbs 英国脱欧时间 铜价格走势图 今日原油 缩表 chaobi 阿希币 美国股市休市 油价 ok币 金子价格 jinjia 抚州 凌晨 男子 千元 妻子 前夫 消防员 左眼 wifi 这些 百搭 火锅 东来 揭秘 什么 腾讯 苹果 测试版 系统 流畅 Note9 升级 越来越 招办 在京 哪个 质量 注意事项 天一 空砍 中乒 出局 中国队 一偷 事故 重磅 中方 效率 27起 南海 少将 表现 预期 白永祥 高明 刘强 包裹 砍掉 百思 买大 降价 京东 公开 台湾 轻工 好不好 俄罗 产业 超萌 闭幕 六一 史上 外汇交易知识 50个基点是多少 以太坊 白银 明斯基时刻 黄金k线走势图 兑美元 国际油价预测 adx指标 国际油价行情 第六计 ifo 小蚁股 白银行情 亚马逊财报 最新国际油价 黄金的价格 银价分析 早晨之星
-
朋友 美元价格 马来西亚货币 ok交易所 专家 汇率日元 养老 业界 ppi指数 港币兑美元 美元兑日元 加拿大就业 中期选举 恒生指数实时 推荐 2199美元 加拿大元汇率 希腊公投 fx57 今日复明日 币世界 jinjia 反对 50个基点是多少 海曼明斯基 dp1s 2012年金价走势 波场tron 美圆指数 今夏 5 绿天鹅 美元指数k线图 玩客币行情 29美元 ok币 适合 513部队 攻坚战 印度卢比 极路由hiwifi 万亿 回升 金价格走势图 金条价格走势 港币兑换美元 元宝币交易 币世界快讯 火币pro 北约 经济 stdaily fx1800 俄罗斯火星人 发表声明 仅数 黄金k线走势图 隔夜利率 外汇趋势分析 btc挖矿 钻石底 你吗 1199元 明细 下周美元走势 白银价格趋势 国际原油走势 即时外汇牌价 原油最新价格 杨林科 meiyuan 玩客云 永久 火锅 福汇外汇 19929日元 12334 缩表 道琼指数 国际石油价格 3号 农场 宽松 首登 8页 蜡烛图 意大利国债 加币汇率 gasstation fx 门罗币 油价走势 莱茨狗 微比特 有限公司 日本 市场黄金价格 美原油行情 瑞士货币 w底形态 持仓报告 圈牌 香港恒生指数 usdt 美元日元汇率 空投 金句 中青 完全 不会 在朝 国防部 用户 鲜 财务省 效率 查询 体育用品 国际石油行情 比特币挖矿机 火币比特币 铜价 zbcom btcc 安币交易所 强国 罪证 40年 老赖 胳膊 3人 对比 匹克 着力点 姐姐 定向 白银 金价走势分析 今日石油价格 白银套利 bullish 今日恒生指数 白银价格分析 黄金etf是什么 coinex 后座议员 外汇走势 美原油连 比特币白皮书 外汇学习 英国股市指数 btcchina 间谍车 谦益农业 军事 介入 高陵区 交警 处去 在京 加深 突尼斯 国企 魅族 三款 九段 班主任 今天美元走势 明斯基时刻 行情报价 房价指数 黄金行情走势 著名财经 欧债 白银价钱 全球主要股指 币安币 外汇交易头寸 道琼斯指数 cbt 币种 最新黄金价格 美元指数走势 金价 otc交易平台 驱逐舰 写下 靓号 成鬼 驾校 救援 腾讯 买单 怎样 他来 创造 核实 29日 京东 包括 汇率欧元 关于黄金交易 台湾一周重点 拜登大胜 德国30 基本面分析 pEE币 黄金年走势图 外汇止损多少 日圆汇率 中币交易所 全球货币战争 hc币 日元美元 国际油价趋势 公信宝 金子价格 林嘉鹏 加剧 不顺 增强 产品 Xperia 搭载 代人 写入 普爱 东部 加征 遂溪 大立光股票 港币汇率 黄金价格表 三大评级机构 g7集团 道琼斯期货 库尔德公投 美国国债利率 wti原油价格 黄金市场行情 全球股市指数 伦敦银走势图 欧元兑美金 vshen 硬币回收价表 美国股市休市 吊销 当天 部门 草色 发布 地方 顺序 打我 大教堂 制服 又放 千万 活动 原油wti 单均线交易 白银行情 今日日历 eos什么意思 币易 俄罗斯汇率 股市行情图 大立光







