前言
今天我们来学习下一款非常有意思的插件PDF.js,正如它的名字一样,它是由Mozilla开源的,用来在Web浏览器上预览PDF文件的一款插件。
我们知道,目前很多浏览器已经支持了PDF的在线预览及下载功能,但是展示的样式各异,并且一部分手机浏览器并不支持预览,在开发WebApp遇到预览PDF的功能,可能需要下载下来借助手机第三方软件打开,显然用户体验不够友好。
而PDF.js恰恰解决了以上问题。
如果说PDF.js的缺点,那大概就是它不支持IE 8 及以下浏览器。(PS:PDF.js使用了HTML 5的相关技术,如canvas,理论上不支持HTML 5的浏览器均不能使用)
项目地址:https://mozilla.github.io/pdf.js/
正文
分析
我们来了解并使用下这款插件。
根据上面地址,我们可以下载PDF.js的预编译版本和源码。
PDF.js提供的预编译版本是可以直接使用的,当然我们也可以使用源码自己编译生成PDF.js插件。
我们来简单说下PDF.js预编译版本的基本构成,截止当前,我使用的是 Stable(v2.0.943) 的预编译版本。
它的结构如下:
1 | ├── build/ |
主要由build和web包构成,build包里的pdf.worker.js是PDF.js的核心处理包,web包里的viewer.js viewer.html viewer.css 用来在Web页面上展示渲染PDF(边框,工具栏等)。
images文件夹里存放一些工具栏图标等内容,locale文件夹里存放各地区语言包。
debugger.js 是debug相关js,我们在使用时可以开启debug输出某些信息来进行调试。
compressed.tracemonkey-pldi-09.pdf 是一个PDF测试类,当我们没有加载自己定义的PDF时,会默认加载此文件。
使用
集成PDF.js插件
我们在SpringBoot项目下引入PDF.js插件并简单使用。
我们新建SpringBoot项目,引入Web模块,生成项目后,我们只需在项目的static文件夹下引入预编译版本的PDF.js即可。
如图:
PS: 为方便管理,我把PDF.js插件放到了一个pdfViewer文件夹里。
我们启动项目,通过浏览器访问viewer.html,即 http://localhost:8080/pdfViewer/web/viewer.html
可以看到成功打开了我们的测试PDF。
我们如果想打开自己的PDF应该如何操作呢?
我们可以在上述网络地址上加上file参数指向我们的PDF。 http://localhost:8080/pdfViewer/web/viewer.html?file=….. 的形式。
我们在项目static文件夹中新建pdf包,放入我们的PDF,如下:
PDF文件在本项目中,访问有两种方式,网络路径访问和相对路径访问。
相对路径访问。
可以看到file后面跟的路径是PDF文件相对于viewer.html的路径。
网络路径访问。
file后面跟的参数是PDF所在的网络地址。
最后预览效果如下图:
如果PDF文件不在本项目中呢?那一定会出现跨域问题,我们来看一下。
访问 http://localhost:8080/pdfViewer/web/viewer.html?file=http://localhost:8081/pdf/IT%E5%85%A5%E8%81%8C%E6%8C%87%E5%8D%97.pdf 可以看到文件不存在。
我们F12查看信息,可以看到PDF.js插件出现如下错误:Uncaught (in promise) Error: file origin does not match viewer's
说明出现了跨域问题。
如何解决呢? 毕竟不是所有的PDF文件都会放在项目中的。
这就需要解决跨域问题,通常有两种方法:
我们如果仍使用路径的方式,则需要对PDF.js进行配置,通过刚才报错的信息,我们很容易在viewer.js 里找到如下内容。
这段js很好理解,设置允许跨域的路径,我们把我们的服务器网络路径添加到HOSTED_VIEWER_ORIGINS对象里,http://localhost:8080。
PS: 这里要注意,viewer.js.map 文件里的 HOSTED_VIEWER_ORIGINS 对象也要改变(预编译版本),否则你会看到很奇怪的缓存问题。
但这样仍是不行的,我们尝试访问 http://localhost:8080/pdfViewer/web/viewer.html?file=http://localhost:8081/pdf/IT%E5%85%A5%E8%81%8C%E6%8C%87%E5%8D%97.pdf 可以看到跨域问题仍然存在。
因为要解决跨域问题,服务器也需要进行设置,我们找到8081服务器,添加跨域设置,主要内容如下(SpringBoot项目):
1 |
|
这段代码很好理解,apiAllowOrigins设置成我们请求的服务器地址即可。
这时候我们在访问上面的PDF地址,可以看到PDF被加载出来了。
我们也可以使用流的方式来加载远程的PDF文件,当然也需要设置跨域属性。
上面的CorsConfig.java 在8081服务器上保持不变,我们在该项目里新增一个类PDFController,用于解析文件并返回流。
1 |
|
我这里传入了fileName属性来获取PDF流。
这样在调用PDF.js 插件时,需要先通过ajax获取PDF流拿到PDF,并提供给PDF.js插件。
我们打开viewer.js 找到如下代码。
这个方法是webView初始化方法,如果没有传file属性,就加载默认的PDF文件。
我们把 file = 'file' in params ? params.file : _app_options.AppOptions.get('defaultUrl');
这段代码换成如下:
1 | file = 'file' in params ? params.file : DEFAULT_URL; |
很好理解,就是不用它的默认值,我们传入一个DEFAULT_URL值。
我们新建helper.js,如下:
1 | var DEFAULT_URL = ""; |
并在PDF.js插件的web包下引入helper.js 和 jquery.js。
PS: jquery.js 的引用就使用了一个ajax,如果由其他的ajax方案可以不引入jquery。
并在viewer.html里引入依赖,需要在viewer.js 之前引入。
1 | .....部分代码略 |
设置好后,我们重新启动下服务器,可以看到PDF文件被加载了。
PS:根据上面,我们可以看到当不传入file参数时,PDF.js会加载默认配置,我们可以通过js等控制这个默认配置,让PDF.js每次打开的都是 http://localhost:8080/pdfViewer/web/viewer.html ,但是PDF文件变化,以实现不显示file参数的需求。
PDF.js 插件的个性化需求
我们使用了PDF.js 插件后,可以看到功能很全,什么工具栏,放大缩小,打印,下载等等功能一应俱全,但现实中我们有可能是不需要这么多的。
尤其在WebApp中,我们可能仅仅可以查看PDF即可,偶尔可以有个放大缩小功能,这样应该如何处理呢。
这项功能的实现是非常简单的,我们找到该功能的按钮的button,直接添加hidden属性即可。
1 | ...... |
PDF.js 踩坑记
关于过大PDF无法显示的问题
曾经遇到过测试环境PDF文件预览正常但是生产环境部分过大PDF文件无法打开的问题,后查看请求发现GET请求的range范围为0-65535,判断可能是服务器对 Range 进行了某些特殊限制。
而PDF.js 插件里有一项参数是可以禁止使用Range的,在viewer.js defaultOptions 对象里,如下:
1
2
3
4
5
6var defaultOptions = {
disableRange: {
value: false,
kind: OptionKind.API
}
}我们将属性设置为true得以解决问题。(如果不生效,可能是map文件存在的缘故,需要重新生成viewer.js.map文件)
PDF无法正常显示
如果相对路径无法加载,可尝试网络路径。
如果使用的是网络路径扔无法加载,可以对网络路径进行encode编码在返回给前端调用。
如果仍不可以,可以考虑使用流传输的方式。
关于PDF.js 插件的缓存问题
使用PDF.js插件过程中,你会发现它会缓存看过的PDF的阅读位置,这本是一项人性化的设定,但如果你就是想每次打开PDF文件后从头开始看起,请使用 disableHistory 参数。
它也在defaultOptions对象里,默认false,改为true后每次再打开这个PDF文件时就会从第一页看起。
viewer.js defaultOptions 对象
可以看到PDF.js 插件 defaultOptions 对象里还有很多很多的默认参数,我们都是可以对其进行设置以实现相关功能或者禁用相关功能的。
关于它们的用法,可以参考一些相关文档,这儿就不做过多叙述了。
结语
今天主要介绍了PDF.js 插件的使用,这是一款非常优秀的插件,在WebApp 预览PDF文件时经常会被使用,而且 Mozilla 以后有意将该插件集成到 FireFox 浏览器里,并且通过该插件立志于打造一项Web浏览PDF文件的标准。
让我们拭目以待吧。