需求
使用Vue开发h5,嵌套到Android和IOS的Webview里,需要实现pdf预览和保存功能,预览pdf的功能,我这边使用了三个库,pdf5,pdf.js,vue.pdf,现在把这三个库在app端的坑分享一下。先说预览的,保存的实现等会再说
前置条件
用第三方库访问pdf,很可能会出现跨域的问题,这个需要后端来处理一下。具体怎么处理,自行百度。我用pdf.js访问的时候,尝试过前端解决跨域,可以参考一下
pdf5实现
先说pdf,这个集成和实现都很简单,但是有个问题,页数多的话,一直现在加载中,并不能加载成功,始终在第一页,这个问题暂时没解决,有大佬知道的话可以指点一下
<template>
<div style="height: 100vh">
<div id="pdf-content" style="height: 60vh"></div>
<div class="div-task-button">
<div class="tasks-button" @click="downloadPdf">保存</div>
</div>
</div>
</div>
</template>
// import Pdfh5 from "pdfh5";
// import "pdfh5/css/pdfh5.css";
import pdf from "vue-pdf";
export default {
name: "Pdfh5",
data() {
return {
pdfh5: null,
title: "通知单",
pdfUrl: "", // 如果引入本地pdf文件,需要将pdf放在public文件夹下,引用时使用绝对路径(/:表示public文件夹)
};
},
mounted() {
try {
let orderItem = JSON.parse(this.$route.query.item);
this.title = orderItem.title;
this.pdfUrl = orderItem.pdfUrl ;
} catch (e) {
console.log(e)
}
this.initPdf();
},
methods: {
initPdf() {
this.pdfh5 = new Pdfh5("#pdf-content", {
pdfurl: this.pdfUrl, // pdf 地址,请求的地址需要为线上的地址,测试的本地的地址是不可以的
lazy: true, // 是否懒加载
withCredentials: true,
renderType: "svg",
maxZoom: 3, //手势缩放最大倍数 默认3
scrollEnable: true, //是否允许pdf滚动
zoomEnable: true, //是否允许pdf手势缩放
});
},
downloadPdf() {
console.log("开始下载");
let body = {
url: this.pdfUrl,
};
if (config.isAndroid && window.hesAndroidNative) {
window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));
} else if (config.isIos && window.webkit) {
window.webkit.messageHandlers.openSystemBrowser.postMessage(
JSON.stringify(body)
);
} else {
}
},
},
};
</script>
pdf.js 实现
使用pdf.js实现,需要下载文件包,具体实现参考
vue开发h5页面如何使用pdf.js实现预览pdf
<template>
<div style="height: 100vh">
<iframe id="pdfViewer" title="" width="100%" height="60%"></iframe>
<div class="div-task-button">
<div class="tasks-button" @click="downloadPdf">保存</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Pdfh5",
data() {
return {
pdfh5: null,
title: "通知单",
numPages: undefined,
// 可引入网络文件或者本地文件
pdfUrl: "", // 如果引入本地pdf文件,需要将pdf放在public文件夹下,引用时使用绝对路径(/:表示public文件夹)
};
},
mounted() {
try {
let orderItem = JSON.parse(this.$route.query.item);
this.title = orderItem.title;
this.pdfUrl = orderItem.pdfUrl;
} catch (e) {
console.log(e)
}
const pdfLink = '/web/viewer.html?file=' + encodeURIComponent( this.pdfUrl);
document.getElementById('pdfViewer').src = pdfLink;
// fetch(this.pdfUrl, {
// method: "get",
// mode: "no-cors", //防止跨域
// responseType: "blob",
// })
// .then((response) => response.blob())
// .then((blob) => {
// const blobUrl = URL.createObjectURL(blob);
// console.log("blobUrl", blobUrl);
// const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);
// document.getElementById('pdfViewer').src = pdfLink;
// });
//this.initPdf();
},
methods: {
},
downloadPdf() {
console.log("开始下载");
let body = {
url: this.pdfUrl,
};
if (config.isAndroid && window.hesAndroidNative) {
window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));
} else if (config.isIos && window.webkit) {
window.webkit.messageHandlers.openSystemBrowser.postMessage(
JSON.stringify(body)
);
} else {
}
// this.pdfh5.download("体检通知单");
},
},
};
</script>
刚开始呢,也是遇到了跨域,我想着看看前端能不能处理,先把url转成blob,然后在用pdf.js来加载blob,就是注释掉的那几行代码
fetch(this.pdfUrl, {
method: "get",
mode: "no-cors", //防止跨域
responseType: "blob",
})
.then((response) => response.blob())
.then((blob) => {
const blobUrl = URL.createObjectURL(blob);
console.log("blobUrl", blobUrl);
const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);
document.getElementById('pdfViewer').src = pdfLink;
});
这么说吧,可以实现,但是又出现了另一个问题,因为使用iframe,Refused to display 'http://192.xxxxxx/' in a frame because it set 'X-Frame-Options' to 'deny'.
,这个又需要后台配置
参考文章
VUE使用 iframe 嵌入网页,浏览器报错 x-frame-options deny
这是通义千问上给出的跨域答案,可以作为参考:
使用vue-pdf实现
最好使用
安装,防止报错,这个实现也简单
<template>
<div style="height: 100vh">
<!-- <div id="pdf-content" style="height: 60vh"></div> -->
<!-- <iframe id="pdfViewer" title="" width="100%" height="60%"></iframe> -->
<div class="pdf_wrap">
<div class="pdf_list">
<pdf
v-for="i in numPages"
:key="i"
:src="pdfUrl"
:page="i"
style="display: inline-block; width: 100%"
></pdf>
</div>
<div class="div-task-button">
<div class="tasks-button" @click="downloadPdf">保存</div>
</div>
</div>
</div>
</template>
<script>
// import Pdfh5 from "pdfh5";
// import "pdfh5/css/pdfh5.css";
import pdf from "vue-pdf";
export default {
name: "Pdfh5",
components: {
pdf,
},
data() {
return {
pdfh5: null,
title: "通知单",
numPages: undefined,
// 可引入网络文件或者本地文件
pdfUrl: "", // 如果引入本地pdf文件,需要将pdf放在public文件夹下,引用时使用绝对路径(/:表示public文件夹)
};
},
mounted() {
try {
let orderItem = JSON.parse(this.$route.query.item);
this.title = orderItem.title;
this.pdfUrl = pdf.createLoadingTask(orderItem.pdfUrl);
console.log(" this.pdfUrl", this.pdfUrl);
this.pdfUrl.promise.then((pdf) => {
this.numPages = pdf.numPages;
})
} catch (e) {
console.log(e)
}
// const pdfLink = '/web/viewer.html?file=' + encodeURIComponent( this.pdfUrl);
// document.getElementById('pdfViewer').src = pdfLink;
// fetch(this.pdfUrl, {
// method: "get",
// mode: "no-cors", //防止跨域
// responseType: "blob",
// })
// .then((response) => response.blob())
// .then((blob) => {
// const blobUrl = URL.createObjectURL(blob);
// console.log("blobUrl", blobUrl);
// const pdfLink = '/web/viewer.html?file=' + encodeURIComponent(blobUrl);
// document.getElementById('pdfViewer').src = pdfLink;
// });
//this.initPdf();
},
methods: {
initPdf() {
this.pdfh5 = new Pdfh5("#pdf-content", {
pdfurl: this.pdfUrl, // pdf 地址,请求的地址需要为线上的地址,测试的本地的地址是不可以的
lazy: true, // 是否懒加载
withCredentials: true,
renderType: "svg",
maxZoom: 3, //手势缩放最大倍数 默认3
scrollEnable: true, //是否允许pdf滚动
zoomEnable: true, //是否允许pdf手势缩放
});
},
downloadPdf() {
console.log("开始下载");
let body = {
url: this.pdfUrl,
};
if (config.isAndroid && window.hesAndroidNative) {
window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));
} else if (config.isIos && window.webkit) {
window.webkit.messageHandlers.openSystemBrowser.postMessage(
JSON.stringify(body)
);
} else {
}
// this.pdfh5.download("体检通知单");
},
},
};
</script>
<style scoped>
.pdf_wrap {
background: #fff;
height: 90vh;
}
.pdf_list {
height: 65vh;
overflow: scroll;
}
.div-task-button {
display: flex;
align-items: center;
width: 100%;
justify-content: center;
}
.tasks-button {
display: flex;
background: white;
padding-bottom: 10px;
padding-top: 10px;
border-radius: 20px;
border: 1px solid #4a90e2;
justify-content: center;
color: #4a90e2;
font-size: 16px;
margin: 80px 20px;
width: 100%;
font-weight: 600;
}
</style>
但是运行起来会有问题,
这个是版本的问题,需要修改源码,要把node_modules\vue-pdf\src\pdfjsWrapper.js中第196行注释掉
保存pdf
在pc端很好实现了,但是嵌入到移动端的webview中,包括IOS和android的兼容性之类的问题,不太好实现,最简单的饿一个办法就是Js调用原生app的方法,打开默认浏览器,用浏览器去保存
js方法呢,就是这一段
downloadPdf() {
console.log("开始下载");
let body = {
url: this.pdfUrl,
};
if (config.isAndroid && window.hesAndroidNative) {
window.hesAndroidNative.openSystemBrowser(JSON.stringify(body));
} else if (config.isIos && window.webkit) {
window.webkit.messageHandlers.openSystemBrowser.postMessage(
JSON.stringify(body)
);
} else {
}
// this.pdfh5.download("体检通知单");
},
android端的实现方法呢,就是
//打开系统浏览器
@JavascriptInterface
public void openSystemBrowser(String param) {
Gson gson = new Gson();
Map<String,String> map = gson.fromJson(param, Map.class);
String url = map.get("url");
Log.e("url",url);
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
// 没有可用的浏览器应用程序
Toast.makeText(WebviewBase.this, "没有可用的浏览器应用程序", Toast.LENGTH_SHORT).show();
}
}