ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PDF.js 2.2.228集成实战:老项目PDF预览与避坑指南

PDF.js 2.2.228集成实战:老项目PDF预览与避坑指南 简介pdfjs-2.2.228-dist.rar 是 Mozilla 团队开源的 PDF.js 库 2.2.228 构建分发包专为 Web 前端工程师和需要在线预览 PDF 的开发者准备目标是提供无需浏览器插件的高质量、跨平台阅读体验同时解决原生 PDF 支持差异与界面定制困难的问题。压缩包共包含 402 个文件整体大小约 3.73MB文件类型涵盖 bcmap、properties、js、map、css、png、svg、license、html 等其中 build 目录下的 pdf.js 与 pdf.worker.js 是核心运行脚本web 目录包含默认 UI 的样式和模板bcmap 与 properties 则用于 CJK 等字符映射保障中文内容正确显示。此资源已有 1285 人学习下载适合用于内容管理系统、文档中心、电子合同或在线教育平台。解压后可直接引入核心脚本在 canvas 中渲染 PDF并支持分块加载、文本搜索、书签、表单、注释等互动特性同时项目采用 MPL 许可开发者可灵活定制工具条、全屏模式和多语言界面快速构建跨浏览器、跨平台的高质量阅读体验。 前阵子接了个老项目改造任务里面有个功能是PDF合同在线预览。技术栈还停留在Vue2 webpack3后端直接给文件流。我先去GitHub官方仓库找pdf.js发现release包有一堆版本选了半天最后下载了一个网盘里流传的pdfjs-2.2.228-dist.rar解压完直接扔进项目居然一次就跑通了。既然这包网上还有不少人在找我就把自己集成时的完整过程和踩过的坑写出来希望能帮到正在被PDF预览折磨的兄弟。文章里的方案主要围绕2.2.228这个版本它属于2.x时代比较稳定的一个迭代兼容性不错API调用方式和现在主流的3.x/4.x差别也不大很多老项目用它很合适。1. 先搞清楚这个压缩包是什么1.1 PDF.js 的核心价值与 dist 目录结构PDF.js是Mozilla团队维护的一个开源项目核心就是用JavaScript解析PDF文件然后通过Canvas把每一页渲染出来。也就是说不需要浏览器原生插件也不需要后端先转图片只要一个前端页面就能实现PDF的查看、缩放、翻页、文本选择等功能。pdfjs-2.2.228-dist.rar这个压缩包拆开看其实就是一个发行版distribution里面包含了经过打包压缩后的运行文件一般会有这几个部分pdf.min.js主库文件封装了加载、渲染PDF的全部API。pdf.worker.min.js后台线程文件负责解析PDF数据避免阻塞主线程。web/目录官方内置的一个完整PDF阅读器里面有viewer.html、viewer.js、viewer.css等打开viewer.html就是一套现成的PDF预览界面。cmaps/目录字符映射表处理某些PDF的中文、日文、韩文编码时要用。license、README等说明文件。一开始我不太理解为什么要有pdf.worker.min.js这个独立文件后来查资料才知道PDF文件解析是重计算任务如果放在主线程里做页面会直接卡死。PDF.js把解析工作放到了Web Worker里主线程只负责接收渲染指令和绘制Canvas这样界面交互才能保持流畅。使用的时候必须告诉库去哪里找Worker文件这就是后面讲到的workerSrc配置。1.2 为什么选择 2.2.228 而不是最新版现在PDF.js已经到4.x甚至5.x了为什么还要用2.2.228因为老项目最怕升级带来的兼容性灾难。我那个项目用的Vue2 webpack3直接上最新版pdfjs-dist经常遇到ES6语法被编译得乱七八糟、模块加载方式不匹配的问题。2.2.228是2019年前后发布的版本API稳定体积也相对小在低版本浏览器上表现友好所以网上很多老系统的教程都认准这个版本。另外release包虽然能在GitHub官方仓库找到但很多同事习惯直接下载别人整理好的rar。比如pdfjs-2.2.228-dist.rar这类资源一般都是把官方build目录和web目录整合过一遍去掉了源码和示例装起来更快。不过从非官方渠道下载存在一定风险解压后最好先杀毒同时核对一下文件完整性——可以看压缩包里的README.md或者mainfest信息或者解压后检查pdf.min.js大小是否在1MB左右。网上有些流传版本会被人改过混进广告脚本这一点务必小心。2. 部署方案从解压到页面预览2.1 放置静态资源并调整路径拿到pdfjs-2.2.228-dist.rar之后第一步是解压。如果Windows环境用WinRAR或7-Zip解压密码通常会在下载页的说明里别信那些“rar password cracker”之类的东西——暴力破解工具没几个干净的中过招的人很多。正确操作是把解压出来的文件夹改个名比如pdfjs然后整个丢到项目的静态资源目录。传统项目就放根目录或者/static下Vue项目放public下uni-app项目放static下。放好之后路径引用要小心。比如在普通HTML页面里这样引用script src/pdfjs/pdf.min.js/script script src/pdfjs/pdf.worker.min.js/script如果页面上访问路径时出现404先看资源是不是真的放到了服务器可访问的目录再看项目是不是配置了baseUrl。我遇到过因为Tomcat部署目录层级多了一层导致/pdfjs变成了/项目名/pdfjs所有引用全部失效这种问题检查一下网络请求里资源的完整地址就能发现。2.2 用自带的 viewer.html 实现完整预览如果不需要自己写UI直接用官方自带的viewer是最省事的方式。把web/viewer.html部署到服务器后通过URL参数传入PDF文件地址即可http://localhost:8080/pdfjs/web/viewer.html?file/pdfs/contract.pdf这里注意几点file参数里的地址必须编码尤其是文件名带中文或空格时要调用encodeURIComponent处理。比如var pdfUrl /pdfs/ encodeURIComponent(劳动合同.pdf); var viewerUrl /pdfjs/web/viewer.html?file encodeURIComponent(pdfUrl); window.open(viewerUrl);另外用viewer.html预览时会受同源策略限制。如果PDF文件在另一个域名需要后端在响应头里配置跨域许可或者自己先通过本地接口把PDF下载成Blob再用URL.createObjectURL生成临时地址传给viewer。我常用的做法是fetch(pdfUrl) .then(res res.blob()) .then(blob { const url URL.createObjectURL(blob); const viewerUrl /pdfjs/web/viewer.html?file encodeURIComponent(url); window.open(viewerUrl); });这种方法还能顺便带上请求头适合需要Token认证的下载接口。2.3 自己写一个精简的 PDF 渲染页面如果不想引入整个viewer只想在页面里渲染第一页作为缩略图或者定制特制的预览效果可以自己写。依赖两个JS文件核心代码如下!DOCTYPE html html head meta charsetutf-8 titlePDF.js 2.2.228 渲染示例/title /head body canvas idpdfCanvas/canvas script src/pdfjs/pdf.min.js/script script var url /pdfs/sample.pdf; // 关键告诉PDF.js worker文件的位置 pdfjsLib.GlobalWorkerOptions.workerSrc /pdfjs/pdf.worker.min.js; pdfjsLib.getDocument(url).promise.then(function(pdf) { return pdf.getPage(1); }).then(function(page) { var scale 1.5; var viewport page.getViewport({ scale: scale }); var canvas document.getElementById(pdfCanvas); var ctx canvas.getContext(2d); canvas.width viewport.width; canvas.height viewport.height; return page.render({ canvasContext: ctx, viewport: viewport }).promise; }); /script /body /html在2.2.228版本中getViewport要传对象形式不能像老版本那样直接传数字page.getViewport(scale)不然会报参数类型错误。还有渲染出来的Canvas有时看起来模糊可以把scale设置成window.devicePixelRatio的倍数var scale 1.5 * (window.devicePixelRatio || 1);这样在高DPI屏幕下会更清晰。如果是做多页预览建议一个页面一个Canvas不要把所有页画在同一个Canvas里反复清空重绘性能反而不如直接堆节点。3. 在 Vue/uni-app 项目里集成3.1 Vue2 项目引入 pdfjs-distVue项目更常见的做法是直接用npm包。安装指定版本npm install pdfjs-dist2.2.228然后在组件里引入import pdfjsLib from pdfjs-dist; import workerSrc from pdfjs-dist/build/pdf.worker.min.js; pdfjsLib.GlobalWorkerOptions.workerSrc workerSrc;但这里有个坑webpack3处理pdfjs-dist的ES模块时容易报错尤其是Cannot read property compile of undefined之类的问题。解决办法是在webpack.base.conf.js里把pdfjs-dist加入externals然后通过script标签直接引用pdf.min.js让全局变量pdfjsLib来接管!-- 在 index.html 中直接引入本地文件而不是npm打包 -- script src/static/pdfjs/pdf.min.js/script然后在组件中用window.pdfjsLib。这种方式对我来说最稳可以避开webpack所有的兼容性坑。如果你不想用全局变量也可以试试import * as pdfjsLib from pdfjs-dist但在老工程里成功概率不高。3.2 uni-app 中使用 web-view 加载 vieweruni-app里没法直接跑复杂的Canvas渲染最靠谱的方式是使用web-view组件把官方viewer当成一个网页嵌入。操作步骤如下把pdfjs-2.2.228-dist整个文件夹放到static/pdfjs目录下。在页面中写web-view :srcviewerUrl/web-view在script里拼接地址export default { data() { return { viewerUrl: } }, onLoad(params) { let file encodeURIComponent(/static/pdfs/ params.fileName .pdf); // 注意这个地址是相对于App的本地地址不同平台解析规则不一样 this.viewerUrl /static/pdfjs/web/viewer.html?file file; } }这里最容易翻车的是路径。在App开发环境下web-view访问的本地路径可能和页面相对路径不一致有时候需要写绝对路径有时候又需要前面加baseUrl。我的经验是先在H5端把完整路径跑通再打包到App里进行真机调试用plus.io或者uni.getEnv去动态获取根路径会稳妥些。另外如果PDF是网络地址一定要确认网络地址能被App访问到同时后端允许跨域否则viewer会一直白屏。3.3 动态传入PDF地址的注意事项不管是Vue还是uni-app通过URL参数传PDF地址都有长度限制如果PDF地址很长或者本身又要带Token、签名等查询参数很容易超出浏览器URL上限。更稳妥的做法是后端给一个短码前端拿到短码后再调接口获取真正的下载地址。或者直接用postMessage把地址消息传给viewer内部在viewer里监听消息然后加载。不过这样要改viewer源码维护成本高我个人更推荐用file参数传一个后端生成的一次性地址简单可靠。4. 常见问题排查与避坑清单4.1 Worker相关报错很多朋友刚接触PDF.js时会遇到Failed to fetch dynamically imported module: ... pdf.worker.min.js或者控制台提示“The API version X does not match the Worker version Y”。这通常是因为主文件和worker文件版本不一致。比如主文件是2.2.228worker却是另一个版本的。解决办法确保pdf.min.js和pdf.worker.min.js来自同一个版本目录并且GlobalWorkerOptions.workerSrc路径正确。如果用了pdfjs-dist的npm包还可以这样加载import pdfjsLib from pdfjs-dist; pdfjsLib.GlobalWorkerOptions.workerSrc https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.worker.min.js;但我不太建议生产环境用公共CDN万一CDN挂了预览功能就废了。应该把worker文件放到自己的静态目录再通过相对路径引用。如果是Vue项目放在public目录下直接/pdf.worker.min.js即可。4.2 文件路径、跨域与中文乱码PDF.js本身对跨域要求比较严。用file://协议直接打开本地HTML时经常报file origin does not match viewers这时候必须起一个本地服务器比如npx serve或者python -m http.server 8080。跨域请求PDF文件时如果后端没开Access-Control-Allow-Origin可以通过代理转发。比如开发环境下用webpack的proxy把/pdf-api请求代理到PDF实际所在的服务器。中文内容显示成乱码或者方块一般是缺了cMaps。2.2.228版本的自带包里有cmaps目录初始化时要指定var loadingTask pdfjsLib.getDocument({ url: url, cMapUrl: /pdfjs/cmaps/, cMapPacked: true });这样中文字体才能正确映射。我踩过一次坑是路径忘加了末尾的斜杠结果请求地址变成了/cmapsxxx.bcmap一直404。4.3 npm/Webpack环境兼容性错误集成过程中如果执行npm install或启动开发环境容易遇到两个经典报错。第一个是could not retrieve https://nodejs.org/dist/latest/shasums256.txt: get https://nodejs.org/dist/latest/shasums256.txt: ...这多半是网络问题尤其是某些代理环境或公司内网访问不了nodejs.org。解决方法是切换npm镜像源比如使用国内镜像npm config set registry https://registry.npmmirror.com再重新安装依赖。如果项目里有依赖需要下载node头文件比如node-sass还可以设置node_mirrornpm config set sass_binary_site https://npmmirror.com/mirrors/node-sass/第二个报错是npm run start Cannot find module ajv/dist/compile/codegen这是因为项目里某些依赖如webpack、sass-loader对ajv版本有要求而node_modules里的ajv版本不兼容。一般把node_modules和package-lock.json删掉重新npm install可以解决。如果重装后还报就手动安装指定版本的ajvnpm install ajv6.12.6 --save-dev老项目很多依赖停留在旧版本ajv6比较稳妥。这种环境兼容性问题说实话和PDF.js本身没关系但如果在集成时正好碰到很容易误以为是PDF.js的问题所以一并列出来。4.4 RAR包解压与完整性校验最后再聊聊这个资源包本身。下载pdfjs-2.2.228-dist.rar后如果解压失败可能是压缩包下载不完整。rar和zip不同rar损坏后很难部分解压出来建议下载完先比对文件大小。如果来源是一个带密码的压缩包别去试所谓“rar password cracker”破解风险太高老老实实找原作者要密码。解压后最好检查一下目录里有没有可疑的.exe或者.bat文件正常dist包只会有js、css、html、mcmap等资源出现其他东西要立刻删除。还有一种情况解压出来的文件名带中文或者特殊空格放到服务器上导致URL访问不到。我一般会把整个目录重命名为纯英文比如pdfjs避免后面各种编码问题。5. 几点实战心得在我自己动手用过2.2.228之后最大的感受是这个版本对“老项目”非常友好。它不像新版那样强制要求现代浏览器和模块语法只要能撑起一个canvas和Web Worker就能跑。如果你的项目还在用jQuery、原生JS或者Vue2旧版本构建链这个版本基本可以无缝接入。如果你问我现在新项目该不该用这个版本我建议新项目还是去GitHub上看看最新Release版因为新版在渲染性能、PDF规范支持上改进很大。但如果你手头是维护了三四年的老系统网上又恰好能找到pdfjs-2.2.228-dist.rar这种现成包那就先用它把功能顶起来后面要升级再单独做技术方案。最后再分享一个小技巧PDF.js渲染大文件时尤其上百页的文档不要一上来就渲染所有页可以先用pdf.getPage(1)渲染封面等用户点击下一页时再渲染对应页。同时用PDFPageProxy.cleanup()及时释放VRAM资源不然长时间翻页后内存占用会一直往上涨移动端尤其明显。这个细节就是纯经验了官方文档里提得很少但实战里特别管用。本文还有配套的精品资源点击获取
返回列表