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/[email protected]/cmaps/',
cMapPacked: true
})
return src ;
}
}
复制代码
但我们公司项目包含了非常多的不同字体,还是满足不了需求,我采用了浏览器默认查看pdf方式和插件方式两种同时使用,可以解决99%的需求啦。不过使用插件形式的pdf不要超过200页,不然会加载时间过长导致打不开。
效果图,哈哈有跟没有一样
版权归原作者 booleann 所有, 如有侵权,请联系我们删除。