0


前端 PDF 预览技巧:标签 vs 插件,如何优雅地展示 PDF 文件

前言

pdf 作为一种常用的文档格式,相信很多同学都在项目中遇到过需要预览 pdf 文件的情况。其实实现的方式有很多,包括传统的标签 iframeembed 方式,也可以运用一些插件,例如 pdf.jsvue-pdf 等等,本文将带大家一起探索不同方式的优劣势以及实现方式和应用场景,帮助你更好地选择适合自己项目的方案,让你在项目中轻松展示 pdf 文件。


一、iframe 标签

  1. <iframe>

标签规定一个内联框架。一个内联框架被用来在当前

  1. HTML

文档中嵌入另一个文档。

1.1 属性

属性描述src在 iframe 中显示的文档的 URL。heightiframe 的高度。widthiframe 的宽度。nameiframe 的名称。seamlessiframe 看起来像是父文档中的一部分。srcdoc规定页面中的 HTML 内容显示在 iframe 中。sandbox对 iframe 的内容定义一系列额外的限制。allow-forms(允许在iframe中提交表单)、allow-same-origin(允许iframe与包含它的页面具有相同的源,这意味着它可以访问与包含页面相同的资源)、allow-scripts(允许在iframe中执行脚本)、allow-top-navigation(允许iframe导航到顶级浏览上下文,即顶级窗口)scrollingHTML5 不支持。规定是否在 iframe 中显示滚动条。yes、no、autoalignHTML5 不支持。HTML 4.01 已废弃。 规定如何根据周围的元素来对齐iframe。left、right、top、middle、bottomframeborder-HTML5 不支持。规定是否显示 iframe 周围的边框。longdescHTML5 不支持。规定一个页面,该页面包含了有关 iframe 的较长描述。marginheightHTML5 不支持。规定 iframe 的顶部和底部的边距marginwidthHTML5 不支持。规定 iframe 的左侧和右侧的边距。

1.2 代码实现

  1. <template><div><iframesrc="https://s4.aconvert.com/convert/p3r68-cdx67/agb6w-9i4xt.pdf"width="100%"height="700"/></div></template>

实现效果

在这里插入图片描述


二、embed 标签

  1. <embed>

标签定义了一个容器,用来嵌入外部应用或者互动程序(插件)。

2.1 属性

属性描述height规定嵌入内容的高度。src规定被嵌入内容的 URL。type规定嵌入内容的 MIME 类型。注:MIME = Multipurpose Internet Mail Extensions。width规定嵌入内容的宽度。

2.2 代码实现

  1. <template><div><embedsrc="https://s4.aconvert.com/convert/p3r68-cdx67/abeub-nb503.pdf"type="application/pdf"width="100%"height="700"/></div></template>

实现效果

在这里插入图片描述


小结

使用

  1. <embed>

  1. <iframe>

标签浏览器会自动调用内置的PDF阅读器插件来显示

  1. pdf

内容。相对于使用插件来显示

  1. pdf

文件有各自的优劣势。

优势:

  • 简单易用: 使用 <embed><iframe> 标签可以很容易地嵌入 pdf 文件,而且不需要额外的插件或库;
  • 跨平台兼容性: <embed><iframe> 标签通常具有很好的跨平台兼容性,可以在各种浏览器和设备上正常显示 pdf 文件;
  • 快速集成: 直接使用标签嵌入 pdf 文件可以快速集成到现有的网页中,不需要额外的学习成本。

劣势:

  • 定制性差: 使用 <embed><iframe> 标签显示 pdf文件的定制性相对较差,无法轻松实现一些高级功能,如自定义样式、交互等;
  • 功能受限: <embed><iframe> 标签提供的功能有限,无法实现一些复杂的 pdf 显示需求,如搜索、缩略图预览、无法禁止打印等;
  • 样式控制困难: 使用 <embed><iframe> 标签嵌入的 pdf文件样式控制相对困难,难以实现与网页样式的统一。

三、vue-pdf 插件

vue-pdf 是一个基于

  1. pdf.js

实现用于在

  1. vue

应用程序中显示

  1. pdf

文件的库。它提供了一个

  1. vue

组件,可以轻松地将

  1. pdf

文件嵌入到你的应用程序中,以便用户可以查看和与

  1. pdf

文件交互。

3.1 安装

  1. npm install --save vue-pdf

如果有版本问题可以降低版本,推荐安装以下版本:

  1. npm install --save vue-pdf@4.2.0

3.2 引入注册

  • 局部import pdf from"vue-pdf";exportdefault{components:{ pdf,},};
  • 全局import pdf from'vue-pdf';Vue.component('pdf', pdf);

3.3 常用的属性及事件

  • Props 属性属性描述:srcPDF 文件的 URL。:page要显示的页码。:rotate页面旋转的角度,仅限90度的倍数。 例如:90, 180, 270, 360, …
  • Events 事件事件描述@password (updatePassword, reason)updatePassword: 调用以输入 PDF 密码的函数。reason: 函数调用的原因,可能是 ‘NEED_PASSWORD’ 或 ‘INCORRECT_PASSWORD’@progress Number文档加载进度,范围从 0 到 1。@loaded文档加载完成时触发。@page-loaded Number页面加载完成后触发。@num-pages NumberPDF 的总页数。@error Object发生错误时触发。@link-clicked Number点击内部链接时触发。

3.4 基础使用(单页)

  1. <template><pdfsrc="https://s4.aconvert.com/convert/p3r68-cdx67/a0jo0-a0e3v.pdf"></pdf></template><script>import pdf from"vue-pdf";exportdefault{components:{
  2. pdf,},};</script>

实现效果

在这里插入图片描述


3.5 加载本地pdf文件

如果直接将

  1. pdf

文件放在任意一个文件夹中因为路径问题会加载不出来。所以需要将

  1. pdf

放在

  1. public > static

下(如下图),并用

  1. /static/xxx.pdf

的路径方式进行引用(

  1. /

即已经代表

  1. public

)。

在这里插入图片描述

代码示例

  1. <template><pdfsrc="../../../static/ceshi.pdf"></pdf></template><script>import pdf from"vue-pdf";exportdefault{components:{
  2. pdf,},};</script>

实现效果

在这里插入图片描述


3.6 分页预览(多页)

  1. <template><divid="document"><!-- 分页组件 --><divclass="component"v-show="schedule === 1"><div@click="pageWay('first')"class="headTail">首页</div><div@click="rotateWay('obey')"class="headTail"><el-tooltipclass="item"effect="dark"content="顺时针旋转"placement="top"><istyle="font-size: 18px"class="el-icon-refresh-right"></i></el-tooltip></div><div@click="pageWay('pre')"class="fluctuatePage":style="pageNumber === 1 ? 'cursor: not-allowed;' : ''">
  2. 上一页
  3. </div><divclass="pagination"><inputv-model.number="pageNumber"type="number"class="inputNumber"@input="inputEvent()"/><span> / {{ pageValue }}</span></div><div@click="pageWay('next')"class="fluctuatePage":style="pageNumber === pageValue ? 'cursor: not-allowed;' : ''">
  4. 下一页
  5. </div><div@click="rotateWay('contrary')"class="headTail"><el-tooltipclass="item"effect="dark"content="顺时针旋转"placement="top"><istyle="font-size: 18px"class="el-icon-refresh-left"></i></el-tooltip></div><div@click="pageWay('last')"class="headTail">尾页</div></div><!-- pdf 组件 --><divclass="pdfContent"><pdf:src="pdfUrl"ref="pdf"v-show="schedule === 1":rotate="pageRotate":page="pageNumber"@num-pages="pageValue = $event"@progress="schedule = $event"@page-loaded="pageNumber = $event"@loaded="loadPdfHandler"@link-clicked="pageNumber = $event"id="pdfID"></pdf></div><!-- loading 组件 --><divclass="progress"v-show="schedule !== 1"><el-progresstype="circle":width="60"color="#53a7ff":percentage="Math.floor(schedule * 100)"></el-progress><p>{{ loadingTxt }}</p></div></div></template><script>import pdf from"vue-pdf";exportdefault{components:{
  6. pdf,},data(){const loadingText ="加载文件中,文件较大请耐心等待...";return{remindText:{loading: loadingText,// 加载中提示语refresh:"若卡住不动,可刷新页面重新加载...",// 刷新提示语},loadingTxt: loadingText,// 初始加载提示语pageRotate:0,//旋转角度pageNumber:0,// 当前页数pageValue:0,// 总页数schedule:0,// 加载进度timerId:"",// 定时器 IDpdfUrl:"https://s4.aconvert.com/convert/p3r68-cdx67/a2mdb-i5v70.pdf",// pdf 文件路径};},// 在组件销毁时清除定时器destroyed(){clearInterval(this.timerId);},mounted(){this.prohibit();this.timerId =setInterval(()=>{// 设置定时器,每隔一段时间切换加载提示语this.loadingTxt ===this.remindText.refresh
  7. ?(this.loadingTxt =this.remindText.loading):(this.loadingTxt =this.remindText.refresh);},4000);this.listenerFunction();// 调用监听滚动条事件的方法},methods:{// 监听输入事件inputEvent(){// 输入页数大于总页数时,设置为总页数,输入页数小于 1 时,设置为 1this.pageNumber = Math.max(1, Math.min(this.pageNumber,this.pageValue));},// 上一页、下一页、首页、尾页事件pageWay(val){if(val ==="pre"&&this.pageNumber >1){this.pageNumber--;// 上一页}elseif(val ==="next"&&this.pageNumber <this.pageValue){this.pageNumber++;// 下一页}elseif(val ==="first"){this.pageNumber =1;// 首页}elseif(val ==="last"&&this.pageNumber <this.pageValue){this.pageNumber =this.pageValue;// 尾页}this.toTop();// 滚动到顶部},// 顺/逆旋转rotateWay(val){this.pageRotate += val ==="obey"?90:-90;},// 滚动顶部toTop(){const container = document.getElementById("document").parentElement;
  8. container.scrollIntoView({behavior:"smooth",block:"start"});},// 监听 pdf 加载完成事件loadPdfHandler(){this.pageNumber =1;// 加载 pdf 时,设置当前页数为 1},// 禁止特定的操作prohibit(){// 禁用右键菜单
  9. document.oncontextmenu=function(){returnfalse;};// 禁用按键
  10. document.onkeydown=function(e){// 定义需要禁用的按键码数组const forbiddenKeys =[65,67,73,74,80,83,85,86,117,18,123];// 判断按下的按键是否在禁用数组中,如果是则返回 false 禁用按键if(e.ctrlKey && forbiddenKeys.includes(e.keyCode)){returnfalse;}};},// 监听滚动条事件listenerFunction(e){
  11. document
  12. .getElementById("document").addEventListener("scroll",function(event){// 监听滚动事件
  13. console.log(event);});},},};</script><stylescopedlang="less">#document{overflow: auto;min-height: 100vh;width: 100%;display: flex;position: relative;.component{user-select: none;color: #ffffff;position: fixed;bottom: 5%;left: 50%;margin-left: -250px;display: flex;align-items: center;justify-content: space-around;background:rgba(0, 0, 0, 0.7);border-radius: 30px;width: 420px;padding: 15px 40px;z-index: 99;.pagination{position: relative;top: 1px;.inputNumber{border-radius: 5px;border: 1px solid #8b8b8b;width: 36px;height: 16px;text-align: center;background: transparent;}.inputNumber:focus{border: 1px solid #00aeff;background:rgba(18, 163, 230, 0.1);outline: none;transition: 0.2s;}}.headTail{border-radius: 50%;display: flex;align-items: center;justify-content: center;}.fluctuatePage{border-radius: 50%;display: flex;align-items: center;justify-content: center;}.fluctuatePage:hover,
  14. .headTail:hover{transition: 0.3s;color: #409eff;cursor: pointer;}}.pdfContent{width: 100%;}.progress{width: 222px;position: absolute;top: 50%;left: 50%;margin-left: -111px;text-align: center;}.progress p{color: #199edb;font-size: 14px;}}/*在谷歌下移除input[number]的上下箭头*/input::-webkit-outer-spin-button,
  15. input::-webkit-inner-spin-button{-webkit-appearance: none !important;margin: 0;}/*在firefox下移除input[number]的上下箭头*/input[type="number"]{-moz-appearance: textfield;}</style>

实现效果

在这里插入图片描述


3.7 打印文件

  1. vue-pdf

中,

  1. print(dpi, pageList)

方法用于触发打印

  1. pdf

文件的功能。

  • dpi:表示打印的分辨率(每英寸的点数)。这个参数用于设置打印时的图像质量,通常可以设置为一个整数值,例如 150300 等。较高的 dpi 值会产生更清晰的打印效果,但也会增加打印文件的大小。
  • pageList:表示要打印的页面列表。这个参数是一个数组,用于指定要打印的页面序号。例如,如果你想打印第 1 页和第 3 页,可以将 pageList 设置为 [1, 3]。如果想打印所有页面,可以使用类似于 [1, 2, 3, ...] 的方式来表示所有页面。
  1. // 设置打印分辨率为 150dpi,打印第1页和第3页this.$refs.myPdfComponent.print(150,[1,3]);
  1. <template><div><button@click="printPdf">打印 pdf</button><pdfref="myPdfComponent":src="pdfSrc"></pdf></div></template><script>import pdf from"vue-pdf";exportdefault{components:{
  2. pdf,},data(){return{pdfSrc:"https://s4.aconvert.com/convert/p3r68-cdx67/a1eqq-4rahm.pdf",};},};</script>

实现效果

在这里插入图片描述


3.8 加密文件

如果你的

  1. pdf

文件是加密的,那么就可以调用

  1. password

方法。

  • updatePassword这个函数用于更新 pdf 文件的密码。当用户输入了正确的密码后,可以调用 updatePassword 函数来更新 pdf 文件的密码。
  • reason这是一个字符串,表示需要密码的原因。通常会包含一些提示信息,告诉用户为什么需要输入密码才能查看 pdf 文件。
  1. <template><pdf:src="pdfUrl"@password="handlePassword"/></template><script>import pdf from"vue-pdf";exportdefault{components:{
  2. pdf,},data(){return{pdfUrl:"https://s4.aconvert.com/convert/p3r68-cdx67/a9tag-qf2jw.pdf",};},methods:{handlePassword(updatePassword, reason){if(reason ==="NEED_PASSWORD"){// 这里可以提示用户输入密码,然后使用输入的密码来更新PDF文档let password =prompt("请输入PDF文档的密码:","");updatePassword(password);}},},};</script>

实现效果

在这里插入图片描述


3.9 无法显示中文内容

一般情况下,是不会出现显示不了中文的问题。但不排除一些特殊的文档,例如票据、合同这类。

  1. <template><pdf:src="pdfUrl"/></template><script>import pdf from"vue-pdf";exportdefault{components:{
  2. pdf,},data(){return{pdfUrl:"",};},methods:{getPdfPort(){// 解析 PDFconst taskData = pdf.createLoadingTask({url: res.data,cMapUrl:"https://cdn.jsdelivr.net/npm/pdfjs-dist@2.5.207/cmaps/",cMapPacked:true,});// 把解析后的地址进行赋值this.pdfUrl = taskData;},},};</script>

上面代码通过

  1. pdf.createLoadingTask()

方法创建一个任务,该任务负责加载和解析指定的

  1. pdf

文件。通过这个方法,可以异步地加载

  1. pdf

文件并获取解析后的数据,以便在页面中显示

  1. pdf

内容。

  • pdf.createLoadingTask() 方法参数参数描述url要加载的 pdf 文件的 url。cMapUrl字符映射文件的 url,用于将 pdf 文件中的字符编码映射到可显示的字符。cMapPacked布尔值,指示是否使用压缩的字符映射文件。

拓展

后台返回文档流

很多时候,考虑到安全性、实时性,后台可能不一定会直接返回一个

  1. url

链接,而是会返回一个流文件(如下图所示),这个时候就需要对原始的流文件进行处理后再进行赋值。

在这里插入图片描述

实现代码

  1. <template><div><button@click="pdfWay">预览pdf</button><iframe:src="item.iframeUrl"width="100%"height="550"/></div></template><script>exportdefault{data(){return{iframeUrl:"",};},methods:{pdfWay(){previewPort().then((res)=>{// 接口方法var binaryData =[];
  2. binaryData.push(res);this.iframeUrl = window.URL.createObjectURL(newBlob(binaryData,{type:"application/pdf"}));});},},};</script>

在上面的代码中,

  1. new Blob(binaryData, { type: "application/pdf" })

会创建了一个

  1. Blob

二进制对象,

  1. binaryData

是一个包含

  1. pdf

文件二进制数据的数组,

  1. { type: "application/pdf" }

指定了这个

  1. Blob

对象的类型为

  1. pdf

文件类型。然后我们调用了

  1. createObjectURL()

方法,该方法接受一个

  1. Blob

对象作为参数,并返回一个包含该

  1. Blob

对象数据的

  1. url

。这个

  1. url

是一个临时的、唯一的

  1. url

,可以用于在浏览器中直接访问和展示

  1. Blob

对象的内容。通过这个

  1. url

,我们就可以直接在页面上展示

  1. pdf

文件的内容。

标签: 前端 vue

本文转载自: https://blog.csdn.net/Shids_/article/details/141401073
版权归原作者 水星记_ 所有, 如有侵权,请联系我们删除。

“前端 PDF 预览技巧:标签 vs 插件,如何优雅地展示 PDF 文件”的评论:

还没有评论
关于作者
...
overfit同步小助手
文章同步