PDF 生成(1)— 开篇

当学习成为了习惯,知识也就变成了常识。 感谢各位的 关注点赞收藏评论

新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn

文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。

简介

本系列旨在介绍纯前端技术方案下的 PDF 生成最佳实践。内容涵盖业务背景、选型思路和实践历程,从简单的 PDF 文件生成到复杂的配置化与服务化。

整个实践过程以技术为驱动,同时也展示了如何打造技术产品的过程。是一份适合任何人实践的教程。

背景

需求来自业务对公司战略的拆解 --- 安全运维托管服务,为用户提供全日制的数字化资产安全运维、监控、告警、专家分析等服务。一句话总结就是,用户付钱找我们为用户提供全方位的资产运维服务。

在这个服务中我们为用户做了很多事情,我们需要让用户看到我们的价值,所以会以日报、周报的形式为用户推送运维报告 ,而这份报告就是以 PDF 文件的形式呈现。

所以,这份报告承载了产品能力和价值的传递,业务对 PDF 文件内容的展现提出了明确的要求:需要呈现出色彩鲜明、精美的设计,简单描述就是好看 + 酷炫

于是,设计同学的设计稿就来了

本系列出现的所有和托管服务相关的配图版权均归 360 企业安全云所有

看到设计稿的瞬间,就在想,这效果用 PDF 能呈现?最后会不会是这结果?

因此,业务需求可以归纳为一份出色、惊艳的 PDF 文件

技术调研

讲了业务背景,接下来就该技术调研了,经过调研,PDF 文件生成可以总结为两大类:原生方案和转化方案。

原生方案

利用开源工具库直接操作 PDF 文件,在文件内绘制内容,比如 iText、PDFKit、pdf-lib。

  • 优点,性能高,适用于内容简单的场景
  • 缺点,难以处理具有复杂排版和样式的场景

转化方案

将内容通过中间媒介转化成 PDF 文件,主要包括:Word 转 PDF、HTML/CSS 转 PDF。

Word 转 PDF 的缺点和原生方案一样,在复杂排版和样式场景上有心无力。大概原理是通过 Word 提供的 API 操作编写 Word 文档,然后 Word 转换成 PDF 文件。

HTML/CSS 转 PDF,主要有如下三种方案:

  • 模版引擎,利用模版引擎生成 HTML/CSS,然后结合下面的两个方案生成 PDF 文件,一般后端同学会用这个方案
  • Canvas,前端常用的方案,例如 html2canvas + jsPDF,但在 PDF 分页、内容截断问题上难以解决,PDF 目录页不支持页面跳转和展示页码
  • 浏览器打印系统 ,利用浏览器的布局、渲染、打印能力,通过 DevTools 协议控制 Chrome/Chromiun,实现 PDF 文件的打印,即 chrome 浏览器右键 -> 打印的自动化版本

技术决策

经过调研和众多方案的分析,最终我们选择了浏览器打印系统 方案,具体的实现上我们选择了 Puppeteer 框架,它是一个 Node.js 库,提供高级 API 控制 Chrome/Chromiun 浏览器,我们在浏览器中手动执行的大多数操作它都可以完成,例如执行 page.pdf 方法即可将当前渲染的页面打印成 PDF 文件,简单易用。

为什么选择基于浏览器打印系统的 puppeteer 方案?

  • 经过方案调研之后的综合对比,基于浏览器打印系统的方案更符合业务的诉求
  • 我们是前端团队,这套方案更符合团队的技术栈
  • 人力和时间成本,其他几个方案基本上就是只能服务端同学自己做,前端很难参与进去,对服务端团队的研发资源造成压力,影响部分业务的吞吐率

这套方案前后端同学各司其职、通力合作,分别做自己擅长的事。服务端同学开发页面接口供前端同学调用,前端同学负责开发酷炫的页面,PDF 生成服务将前后端同学开发的页面转成 PDF 文件

于是,产品和设计同学就可以在这张静态的 A4 纸上尽情发挥,不受技术限制。

技术架构

方案的技术架构,分为三大块,分别是:

  • 接入方,即 PDF 生成服务的调用方,就是一个普通的 Web 项目(前端 + 后端)
  • PDF 生成服务,对外暴露 API,一次 API 调用,产出一份 PDF 文件的下载地址
  • 配置服务,维护接入方的信息,为 PDF 生成服务提供必要的配置信息,比如接入方 Web 项目的页面地址,PDF 生成服务会负责将这些页面生成 PDF 文件

整体执行流程如下:

  • 接入方 ,带着分配的 APP ID 和 其它参数调用生成 PDF 服务的 API 接口,其它参数是接入方前后端自己需要用到的参数,调用时提供的所有参数会原封不动的通过 URL 查询参数带到接入方的前端页面地址上

  • PDF 生成服务

    • 接收到请求后,将请求放入队列
    • 监听到队列有内容进入,通知生成 PDF 文件的模块,启动 PDF 生成任务
    • 任务拿着 APP ID 请求配置服务,获取到对应的配置信息
    • 任务将配置信息中指定的所有页面打印成 PDF 文件
    • 将 PDF 文件上传到智慧云(S3)上,并将 PDF 文件的下载地址通过回调接口回传给接入方

总结

到这里本文就结束了,本文主要讲了如下内容:

  • 业务背景,要求技术能够产出一份漂亮 + 酷炫的 PDF 文件
  • 技术调研,主要分为原生方案和转化方案
  • 技术决策,结合业务诉求、各个方案的优缺点、团队技术栈和部门人力、时间成本,最终选择基于浏览器打印系统的 puppeteer 方案
  • 整个方案的技术架构设计

一个完善的技术架构是随着业务持续迭代而产生的,接下来我们将从零开始逐步实现整套架构,因此这是一份适合任何人实践的教程

链接

  • PDF 生成(1)--- 开篇 中讲解了 PDF 生成的技术背景、方案选型和决策,以及整个方案的技术架构图,所以后面的几篇一直都是在实现整套技术架构
  • PDF 生成(2)--- 生成 PDF 文件 中我们通过 puppeteer 来生成 PDF 文件,并讲了自定义页眉、页脚的使用和其中的 。本文结束之后 puppeteer 在 PDF 文件生成场景下的能力也基本到头了,所以,接下来的内容就全是基于 puppeteer 的增量开发了,也是整套架构的核心难点
  • PDF 生成(3)--- 封面、尾页 通过 PDF 文件合并技术让一份 PDF 文件包含封面、内容页和尾页三部分。
  • PDF 生成(4)--- 目录页 通过在内容页的开始位置动态插入 HTML 锚点、页面缩放、锚点元素高度计算、换页高度补偿等技术让 PDF 文件拥有了包含准确页码 + 页面跳转能力的目录页
  • PDF 生成(5)--- 内容页支持由多页面组成 通过多页面合并技术 + 样式沙箱解决了用户在复杂 PDF 场景下前端代码维护问题,让用户的开发更自由、更符合业务逻辑
  • PDF 生成(6)--- 服务化、配置化 就是本文了,本系列的最后一篇,以服务化的方式对外提供 PDF 生成能力,通过配置服务来维护接入方的信息,通过队列来做并发控制和任务分类
  • 代码仓库 欢迎 Star

当学习成为了习惯,知识也就变成了常识。 感谢各位的 关注点赞收藏评论

新视频和文章会第一时间在微信公众号发送,欢迎关注:李永宁lyn

文章已收录到 github 仓库 liyongning/blog,欢迎 Watch 和 Star。

相关推荐
GIS之路4 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug8 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213810 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中31 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路35 分钟前
GDAL 实现矢量合并
前端
hxjhnct37 分钟前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星1 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端