PDF预览完整解决方案及各种兼容(VUE版)
Posted booleann
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PDF预览完整解决方案及各种兼容(VUE版)相关的知识,希望对你有一定的参考价值。
PDF预览完整解决方案及各种兼容(VUE版)
2021年11月12日 16:57 · 阅读 2547
一、利用iframe
就一行代码就够了,只能满足最基本的浏览,且会出现很多问题。
<iframe src="Url" style="width:100%;height:100%" frameborder="0"></iframe>
复制代码
**问题一缓存问题:**利用iframe打开pdf后,当再次利用iframe打开另一个pdf时会显示第一份pdf,原因是浏览器对url的缓存处理。
**解决办法:**给url加时间戳或随机数,这样就没有缓存问题了。
const fresh=new Date().getTime();//时间戳 this.url = this.url+'?'+ fresh; // 初始化查看pdf应用地址
复制代码
**问题二使用base64url问题:**有些pdf的url采用base64格式,直接将base64格式url放进src中可能会报错导致显示不了。原因是base64地址太长,浏览器不支持。
**解决办法:**利用Blob转base64url成文件路径.
var bstr = window.atob(_this.baseUrl); //解码 var n = bstr.length; var u8arr = new Uint8Array(n); while (n--) u8arr[n] = bstr.charCodeAt(n); //转二进制 let blob = new Blob([u8arr], type: 'application/pdf' ); //用blob生成pdf文件,返回PDF文件 this.url = window.URL.createObjectURL(blob); //得到的文件路径url
复制代码
**问题三特殊字体和水印无法显示:**这里采用插件的形式解决
二、利用vue-pdf插件
这里以VUE框架为例。vue-pdf是基于pdfjs-dist插件的vue封装。不是vue框架可以去找pdfjs-dist对应的封装或者直接用pdfjs-dist,不过pdfjs-dist使用稍微复杂些。
第一步 安装 npm install --save vue-pdf
第二部引入注册
import VuePdf from "vue-pdf";
export default
components:
VuePdf,
,
第三步 使用
<VuePdf src="PDFurl" : />
上面是最简单的使用方式,只能显示第一页的pdf,满足不了大部分需求,现在增加功能
模板里
<VuePdf v-for="i in numPages" :key="i" :src="url" :page="i" />
下面方法在mounted里面使用
// PDF初始化 getNumPages() let loadingTask = VuePdf.createLoadingTask(this.url, ); loadingTask.promise.then(pdf => this.numPages = pdf.numPages; ).catch(err => console.error('pdf 加载失败', err); ) ,numPages、url在data里面定义为空,在getNumPages()调用前将路径赋值给this.url
复制代码
这样就可以得到一个可以pdf的全部内容,pdf放大缩小翻页就不赘述了百度很多
PDF下载
<div @click="down(pdfName)">下载</div>
//需要两个参数 pdfName 和 pdf的base64地址。
在调用方法前将pdf的base64地址赋值给this.baseUrl就可以调用方法下载。
down(pdfName)
const fileName = pdfName;
let byteCharacters = atob(this.baseUrl);
let byteNumbers = new Array (byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++)
byteNumbers[i] = byteCharacters.charCodeAt(i);
let byteArray = new Uint8Array ( byteNumbers);
let blob = new Blob([byteArray], type:"application/pdf");
if (navigator.msSaveOrOpenBlob)
navigator.msSaveBlob(blob,fileName);
else
let link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
,
复制代码
VUE-PDF出现问题一:部分pdf水印不显示
解决办法
+ 步骤一 在node_modules/pdfjs-dist/build/pdf.worker.js注释掉一行代码
+ if (data.fieldType === "Sig")
+ data.fieldValue = null;+ // 注释掉底下这行 就可以显示电子签章
+ // this.setFlags(_util.AnnotationFlag.HIDDEN);+
+ 步骤二 在node_modules/pdfjs-dist/es5/build/pdf.worker.js注释掉一行代码
+ if (data.fieldType === "Sig") + data.fieldValue = null;
+ // 注释掉底下这行 就可以显示电子签章+ // _this4.setFlags(_util.AnnotationFlag.HIDDEN);
+
复制代码
问题又来了,在node_modules里面改动文件下一次打包或者项目上线就行不通了。
这里采用把vue-pdf项目文件放在服务器tomcat的一个文件夹下。项目里用iframe来直接跳转的形式来显示
<iframe :src="iframeUrl" frameborder="0"></iframe>
const fresh=new Date().getTime();//时间戳
this.iframeUrl = location.origin + "/pdf/index.html"+'?'+ fresh; // 初始化查看pdf应用地址
复制代码
那么在服务器下的vue-pdf文件如何得到项目上传过来的PDF信息呢,这里采用indexDB数据库。这里不推荐使用cookies和localStorage的形式存储数据,因为PDF数据可能会很大,另外两种形式容量不够。
IndexDB存储PDF需要的信息
在上线项目里使用
/** *@param url pdf地址
*@param baseUrl pdfbase64地址
* @param fileName 文件名
**/
setIndexDB(url,baseUrl,fileName)
// 创建indexDB数据库 //pdfDB
var request = window.indexedDB.open('pdfData');
request.onerror = function()
console.log('数据库打开失败'); ;
request.onsuccess = function(e)
var pdfDB= e.target.result;
var store = pdfDB.transaction('workers','readwrite').objectStore('workers');
store.put( id: 1, pdfUrl: url,baseUrl:baseUrl,pdfName:fileName);
;
request.onupgradeneeded = function(e)
// 在数据库中创建该对象空间,workers相当于表的名字
e.target.result.createObjectStore('workers', keyPath:'id');
,
复制代码
在vue-pdf里面获取存储的数据
getPdfUrl() const _this = this; const request = window.indexedDB.open("pdfData"); request.onerror = function () console.log("数据库打开失败"); ; request.onsuccess = function (e) // var store = e.target.result.transaction('workers','readwrite').objectStore('workers'); // store.put(id:1,pdfUrl:"2.pdf",pdfName:"",baseUrl:"",pathType:"路径形式") const readPDF = e.target.result .transaction(["workers"]) .objectStore("workers") .get(1); readPDF.onsuccess = () => if (readPDF.result) _this.url = readPDF.result.pdfUrl; _this.baseUrl = readPDF.result.baseUrl; _this.pdfName = readPDF.result.pdfName; var bstr = window.atob(_this.baseUrl); //解码 var n = bstr.length; var u8arr = new Uint8Array(n); while (n--) u8arr[n] = bstr.charCodeAt(n); //转二进制 let blob = new Blob([u8arr], type: 'application/pdf' ); //用blob生成pdf文件,返回PDF文件 let path = window.URL.createObjectURL(blob); _this.iframeUrl = path; // 初始化查看pdf应用地址 _this.getNumPages(); else console.log("未获得数据记录"); ; ; request.onupgradeneeded = function (e) // 在数据库中创建该对象空间,workers相当于表的名字,表名不可随意更改 e.target.result.createObjectStore("workers", keyPath: "id" ); ; ,
复制代码
具体indexDB看文档www.ruanyifeng.com/blog/2018/0…
vue-pdf项目例子看gitee.com/tianguai/vu…
问题二:字体缺失
VUE-PDF会有一些特殊字体显示不了,这是由于node_modules/pdfjs-dist/cmaps路径下没有对应的字体文件
解决方法:在vue-pdf项目例子中加入以下代码,这里引入外部字体库。
computed:
pdfSrc()
//处理pdfUrl返回
let src = pdf.createLoadingTask(
url: this.pdfUrl,
//引入pdf.js字体,templ
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.5.207/cmaps/',
cMapPacked: true
)
return src ;
复制代码
但我们公司项目包含了非常多的不同字体,还是满足不了需求,我采用了浏览器默认查看pdf方式和插件方式两种同时使用,可以解决99%的需求啦。不过使用插件形式的pdf不要超过200页,不然会加载时间过长导致打不开。
效果图,哈哈有跟没有一样
前端在线预览PDF文件
前言
这里用到了vue-pdf
插件,预览PDF相关的操作基本都有实现;
我们需要做的就是各种布局(因为需兼容已有的布局,有的地方可能需要修改),比如翻页按钮,页码展示等等;
vue-pdf的GitHub地址:FranckFreiburger/vue-pdf: vue.js pdf viewer (github.com)
目录
- 入门例子
- 展示所有页码
- 翻页操作
- 封装组件
- 完整代码
正文
1. 入门例子
安装命令:yarn add vue-pdf
最简单的入门例子,如下所示:
<template>
<pdf src="/pdf/1.pdf"></pdf>
</template>
<script>
import pdf from \'vue-pdf\'
export default
components:
pdf
关于本地文件的路径问题:
这里需要注意一下,要把pdf放在public目录下,然后通过/
进行引用;
比如你的pdf路径为:public/pdf/1.pdf
,那么src就要写成:/pdf/1.pdf
;
如果是远程路径,则直接赋值;
2. 展示所有页码
上面的入门例子只是展示了第一页的内容,其他内容没有展示,如果需要展示其他页,则需要添加翻页功能;
但是现在我们先简化,不添加翻页功能,而是用v-for直接展示所有的页码;
<template>
<div>
<pdf
v-for="i in numPages"
:key="i"
:src="src"
:page="i"
></pdf>
</div>
</template>
<script>
import pdf from \'vue-pdf\'
var loadingTask = pdf.createLoadingTask(\'/pdf/1.pdf\');
export default
components:
pdf
,
data()
return
src: loadingTask,
numPages: undefined,
,
mounted()
this.src.promise.then(pdf =>
this.numPages = pdf.numPages;
);
</script>
展示效果如下所示:
当我们的页码不是很多时,可以采用这种简单粗暴的方式进行展示,很方便;
但是如果页码过多,则不仅看起来很费劲,而且加载也会很慢,这时就需要用到翻页功能;
3. 翻页操作
这里主要增加两个按钮,以及相关属性,下面是部分代码:
<a-list-item>
<div @click="changePdfPage(\'pre\')"
:>
上一页
</div>
</a-list-item>
<a-list-item>
<div @click="changePdfPage(\'next\')"
:>
下一页
</div>
</a-list-item>
<pdf :src="srcPdf"
:page="currentPage"
@num-pages="pageCount=$event"
></pdf>
- @num-pages 事件:获取pdf的总页数,这里获取到之后传给了pageCount
- page 属性:就是当前页码,这里通过点击上一页和下一页来修改来更新页码
效果如下所示:
完整代码见下面;
4. 封装组件
为了方便使用,我们可以将上面的预览代码封装成功一个单文件组件,然后在需要的地方进行引入即可;
封装后的组件代码贴到文末了,因为有点长:
我们在展示pdf文件时,可以通过跳转到新标签页的方式进行展示,这样组件内的布局不会有太大的变化;
跳转代码如下所示:
let routeUrl = this.$router.resolve(
path: \'/preview-pdf\',
query:pdfPath
)
window.open(routeUrl.href, \'_blank\')
-
/preview-pdf
:这个路径就是配置在路由里面的,预览pdf的路径 -
pdfPath
:这里我们是通过query的方式进行传参,然后在预览组件内通过this.srcPdf = decodeURIComponent(this.$route.query.pdfPath)
进行获取;-
因为存在编码问题,所以这里需要加上解码操作;
如果pdf路径是http远程路径,则不需要解码
-
5. 完整代码
完整的封装组件如下,这里是参考网上的例子,做了一些修改
<template>
<div id="container">
<!-- 上一页、下一页 -->
<div class="right-btn">
<a-space>
<a-list>
<a-list-item>
<div >
<input v-model.number="currentPage"
type="number"
class="inputNumber"
@input="inputEvent()"> / pageCount
</div>
</a-list-item>
<a-list-item>
<div @click="changePdfPage(\'first\')"
>
首页
</div>
</a-list-item>
<a-list-item>
<!-- 在按钮不符合条件时禁用 -->
<div @click="changePdfPage(\'pre\')"
:>
上一页
</div>
</a-list-item>
<a-list-item>
<div @click="changePdfPage(\'next\')"
:>
下一页
</div>
</a-list-item>
<a-list-item>
<div @click="changePdfPage(\'last\')"
>
尾页
</div>
</a-list-item>
</a-list>
</a-space>
</div>
<div class="pdfArea">
<pdf :src="srcPdf"
ref="pdf"
:page="currentPage"
@num-pages="pageCount=$event"
@page-loaded="currentPage=$event"
@loaded="loadPdfHandler"
@link-clicked="currentPage = $event"
></pdf>
</div>
</div>
</template>
<script>
import pdf from \'vue-pdf\'
export default
components:
pdf
,
computed:
,
created ()
console.log(\'query:\', this.$route.query)
this.srcPdf = decodeURIComponent(this.$route.query.pdfPath)
,
destroyed ()
,
mounted ()
,
data ()
return
// ----- vuepdf -----
// src静态路径: /static/xxx.pdf
// src服务器路径: \'http://.../xxx.pdf\'
// src: srcPdf,
// 当前页数
currentPage: 0,
// 总页数
pageCount: 0,
// 加载进度
loadedRatio: 0
,
methods:
// 页面回到顶部
toTop ()
document.getElementById(\'container\').scrollTop = 0
,
// 输入页码时校验
inputEvent ()
if (this.currentPage > this.pageCount)
// 1. 大于max
this.currentPage = this.pageCount
else if (this.currentPage < 1)
// 2. 小于min
this.currentPage = 1
,
// 切换页数
changePdfPage (val)
if (val === \'pre\' && this.currentPage > 1)
// 切换后页面回到顶部
this.currentPage--
this.toTop()
else if (val === \'next\' && this.currentPage < this.pageCount)
this.currentPage++
this.toTop()
else if (val === \'first\')
this.currentPage = 1
this.toTop()
else if (val === \'last\' && this.currentPage < this.pageCount)
this.currentPage = this.pageCount
this.toTop()
,
// pdf加载时
loadPdfHandler (e)
// 加载的时候先加载第一页
this.currentPage = 1
,
</script>
<style scoped>
#container
overflow: auto;
font-family: PingFang SC;
width: 100%;
display: flex;
position: relative;
/* 功能按钮区 */
.right-btn
right:4rem;
position: fixed;
display: flex;
flex-wrap: wrap;
justify-content: center;
z-index: 99;
.pdfArea
width: 80%;
/*在谷歌下移除input[number]的上下箭头*/
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button
-webkit-appearance: none !important;
margin: 0;
/*在firefox下移除input[number]的上下箭头*/
input[type=\'number\']
-moz-appearance: textfield;
.inputNumber
border-radius: 8px;
border: 1px solid #999999;
font-size: 18px;
width: 2rem;
text-align: center;
.inputNumber:focus
border: 1px solid #00aeff;
background-color: rgba(18, 163, 230, 0.096);
outline: none;
transition: 0.2s;
</style>
如何使用?
先注册路由:src/router/index.js
import MyPdf from \'../components/MyPdf\'
export default new VueRouter(
routes: [
path: \'/apply-contract-pdf\',
name: \'apply-contract-pdf\',
component: MyPdf
,
])
再通过如下方法进行预览:
previewPdf(pdfPath)
let routeUrl = this.$router.resolve(
path: \'/preview-pdf\',
query:pdfPath
)
window.open(routeUrl.href, \'_blank\')
,
总结
本篇介绍了vue-pdf的一些简单使用,包括首页展示、分页展示等;
其实还有一些进度条展示这里没列出来,感兴趣的可以配合a-progress
组件和progress
属性进行体验
以上是关于PDF预览完整解决方案及各种兼容(VUE版)的主要内容,如果未能解决你的问题,请参考以下文章