【面试题】1715- 面试官:PDF 预览和下载你是怎么实现的?
前言作者: 熊的猫
https://juejin.cn/post/7207078219215732794
在开发过程中要求对 PDF
类型的发票提供 预览 和 下载 功能, PDF
类型文件的来源又包括 H5 移动端
和 PC 端
,而针对这两个不同端的处理会有些许不同,下文会有所提及。
针对 PDF 预览
的文章不在少数,但似乎都没有提及可能遇到的问题,或是提供对应的具体需求场景下如何选择,因此,本文的核心就是结合实际需求场景下,看看目前各种实现方案到底哪一个更适合,当然希望大家可以在评论区对文中的内容进行斧正,或是提供更优质的方案。
基本要求:
-
支持
pdf 文件
内容的 完整预览 -
多页 pdf 文件
支持分页查看
-
PC 端
和移动端
都需支持 下载 和 预览
产品要求:
- PC 端 的预览要支持在 当前页 进行预览
-
pdf 文件
预览时的字体要 和 实际文件的 字体保证一致性
先抛开上面的各种要求,咱们先总结下目前实现 PDF
预览的几种常用方式:
- 借助各种类库,基于代码实现预览,如基于 pdfjs-dist[1] 的包
-
直接基于各个浏览器内置的
PDF
预览插件,如 -
服务端将
PDF
文件转换成图片
接下来分别看看以上方案如何实现,以及是否符合上述提供的要求!
实现预览
标签
元素 将外部内容嵌入文档中的指定位置,此内容由 外部应用程序 或 其他交互式内容源(如 浏览器插件)提供。
说简单点,就是使用 来展示的资源是完全交由它所在的环境提供的展示功能,即如果当前的应用环境支持这个资源的展示那么就可以正常展示,如果不支持那就无法展示。
使用起来也是非常简单:
type="application/pdf"
:src="pdfUrl"
width="800"
height="600" />
多数现代浏览器已经弃用并取消了对浏览器插件的支持,现在已经不建议使用
标签,但可以使用
、
等标签代替。
标签
基于 的方式和以上差不多,整体效果也一致,这里这就不在额外展示:
:src="pdfUrl"
width="800"
height="600" />
值得注意的是,即便使用的是 但实际展开其内层结构后你会发现:
其内部还是 标签?这是怎么回事,不是说最好不建议使用
吗?
首先来在 caniuse[2] 查看兼容情况,如下:
我们再找一个不支持 的浏览器,比如
IE
,来试试效果:
换成 试试,如下:
显然, 在不兼容的环境直接无法显示,而
是能够正常识别的,只不过
加载的资源无法被
IE
浏览器处理,即本质原因是 IE
浏览器根本就不支持对类似 PDF
等文件的预览,比如当尝试直接在地址栏中输入 http://127.0.0.1:3000/src/assets/2.pdf
时会得到:
因此,通常情况下当浏览器不支持内联 PDF
时,应该提供一个 PDF
的回退链接,即以下载的方式来实现,而这就是 pdfobject[3] 做的事情,实际上它的源码内容比较简单,核心就是 PDFObject 会检测浏览器对内联/嵌入 PDF 的支持,如果支持嵌入,则嵌入 PDF,如果浏览器不支持嵌入,则不会嵌入 PDF,并提供一个指向 PDF 的回退链接,例如在 IE
中的表现:
事实上,这其实只是帮我们少写了一些兼容性的代码而已,也不一定符合大部分人的场景,在这里提到只是因为其与 之间存在的联系。
vue3-pdfjs 实现预览
为什么不直接使用 pdfjs-dist
?
pdf.js[4] 几个明显的可吐槽的点:
-
包名称不统一,
npm
上的包名叫pdfjs-dist
,然而在Readme
中自己又称其为pdf.js
-
没有清晰的文档作为指引,只能通过其仓库中的
examples
目录的内容作为参考 -
官方示例不够友好,例如没有提供
vue/react
等相关的示例 - 直接使用需要引入很多文档没有指明的内容
-
有时展示的
pdf
内容文字模糊或缺少部分等 - ...
因此,既然已经有基于 vue/react
封装好的包,这里就直接用来作为演示。
具体使用
安装和使用过程可参考 vue3-pdfjs[5] ,具体 Vue3
示例代码如下: