引言:最近在做微信小程序和手机端app开发(基于vue),实现一个pdf预览功能。
<web-view src="http://cn.createpdfonline.org/pdffiles/test(20211215094117).pdf" >web-view>
总结:使用webview标签在系统上兼容性并不好,不能控制下载状态,而且在阅读状态上只能做到“假控制”。
1. 使用 iframe、embed、新窗口打开
<iframe class="iframe" src="http://cn.createpdfonline.org/pdffiles/test(20211215094117).pdf">iframe>
<embed class="iframe" src="http://cn.createpdfonline.org/pdffiles/test(20211215094117).pdf">
优点:简单,支持大部分 PC 浏览器(IE 不支持)。跨域资源同样可以(无需 cors)
缺点:不支持移动端浏览器,不支持 IE 等低版本浏览器。样式无法自定义。
2. 使用vue中的组件vue-pdf
安装:打开命令行,直接使用npm或者yarn安装
npm install --save vue-pdf
<template>
<div class="pdf_wrap">
<div class="pdf_list">
<pdf :src="src" style="width: 100%" ref="pdf"> </pdf>
</div>
</div>
</template>
<script>
import pdf from 'vue-pdf'
export default {
name: 'myTestOne',
components: {
pdf
},
data () {
return {
src: 'http://cn.createpdfonline.org/pdffiles/test(20211215094117).pdf',
}
},
methods: {
}
}
</script>
<style scoped>
.pdf_wrap {
background: #fff;
height: 100vh
}
.pdf_list {
height: 80vh;
overflow: scroll;
}
</style>
当你运行这段代码时(在pdf链接没过期的情况下),你会惊奇地发现,会出现跨域问题,并报出以下错误:
解决方案一
你需要联系你的后端小伙伴,因为PDF 文件作为静态资源放在 nginx服务器中。
在 nginx.conf 中设置:
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
完整配置如下:
server {
listen 80;
server_name localhost;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
location / {
root pdf;
index index.html index.htm;
}
}
解决方案二(存在问题,后期研究)
和vue 接口请求解决跨域问题一样,在本地开发时同样通过 webpack的devServer去代理pdf预览的url(请求),其他环境(生成及测试)则让后端去解决。
解决开发环境pdf预览跨域问题就是在devServer中添加一个代理即可:
//vue.config.js
proxy: {
//当pdf和数据接口不在同一个请求地址下时,为pdf预览追加一个代理
"/pdf": {
target:"http://cn.createpdfonline.org/pdffiles",
changeOrigin: true,
pathRewrite: {
'^/pdf': ''
}
}
}
运行结果如下:
重新保存项目,这个pdf就会显示在你的眼前,但是别高兴得太早,这个pdf只有一页,所以在这个基础上进行以下优化:
代码实现(多页显示):
<template>
<div class="pdf_wrap">
<div class="pdf_list">
<pdf v-for="i in numPages" :key="i" :page="i" :src="url" style="width: 100%" > </pdf>
</div>
</div>
</template>
<script>
import pdf from 'vue-pdf'
export default {
name: 'myTestOne',
components: {
pdf
},
data () {
return {
src: 'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
numPages:1,
url:''
}
},
mounted: function() {
this.getNumPages()
},
methods: {
getNumPages() {
this.url = pdf.createLoadingTask(this.src)
this.url.promise.then(pdf => {
this.numPages = pdf.numPages // 这里拿到当前pdf总页数
})
},
}
}
</script>
<style scoped>
.pdf_wrap {
background: #fff;
height: 100vh
}
.pdf_list {
height: 100vh;
overflow: scroll;
}
</style>
该代码在pdfjsWrapper.js文件的第196行会报错,(偷摸给注释掉了)
//pdfRender.cancel().catch(function(err) {
//emitEvent('error', err);
//});
代码实现(单页展示,可翻页):
<template>
<div>
<div class="tools">
<bk-button :theme="'default'" type="submit" :title="'基础按钮'" @click.stop="prePage" class="mr10"> 上一页</bk-button>
<bk-button :theme="'default'" type="submit" :title="'基础按钮'" @click.stop="nextPage" class="mr10"> 下一页</bk-button>
<div class="page">{{pageNum}}/{{pageTotalNum}} </div>
<bk-button :theme="'default'" type="submit" :title="'基础按钮'" @click.stop="clock" class="mr10"> 顺时针</bk-button>
<bk-button :theme="'default'" type="submit" :title="'基础按钮'" @click.stop="counterClock" class="mr10"> 逆时针</bk-button>
</div>
<pdf ref="pdf"
:src="url"
:page="pageNum"
:rotate="pageRotate"
@progress="loadedRatio = $event"
@page-loaded="pageLoaded($event)"
@num-pages="pageTotalNum=$event"
@error="pdfError($event)"
@link-clicked="page = $event">
</pdf>
</div>
</template>
<script>
import pdf from 'vue-pdf'
export default {
name: 'Home',
components: {
pdf
},
data() {
return {
url: "http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf",
pageNum: 1,
pageTotalNum: 1,
pageRotate: 0,
// 加载进度
loadedRatio: 0,
curPageNum: 0,
}
},
mounted: function() {},
methods: {
// 上一页函数,
prePage() {
var page = this.pageNum
page = page > 1 ? page - 1 : this.pageTotalNum
this.pageNum = page
},
// 下一页函数
nextPage() {
var page = this.pageNum
page = page < this.pageTotalNum ? page + 1 : 1
this.pageNum = page
},
// 页面顺时针翻转90度。
clock() {
this.pageRotate += 90
},
// 页面逆时针翻转90度。
counterClock() {
this.pageRotate -= 90
},
// 页面加载回调函数,其中e为当前页数
pageLoaded(e) {
this.curPageNum = e
},
// 其他的一些回调函数。
pdfError(error) {
console.error(error)
},
}
}
</script>
<style scoped>
</style>
引言:经上面的测试,发现了以下问题:
在基于vue开发的移动项目中,如果预览的pdf数量不是很多的话,是可以使用 vue-pdf 的,因为在实际开发测试中,ios系统依次预览多个pdf文件会出现白屏的现象,android系统不会出现。但是此时也很难监控阅读状态,所以最后采用pdf.js来实现业务需求。
为了减小打包体积,bulid文件夹中保留pdf.js和pdf.worker.js即可正常编译。
代码实现:
<template>
<div>
<iframe :src="src" style="width: 100%;height: 100vh" ></iframe>
</div>
</template>
<script>
export default {
name: "myTestTwo",
data(){
return {
url:'http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf',
src:''
}
},
mounted() {
this.getUrl();
},
methods :{
getUrl:function () {
this.src = '/pdfplugin/web/viewer.html?file=' + this.url
}
}
}
</script>
<style scoped>
</style>
效果展示:
通过结果可以看出,pdf.js真的是很强大,我们可以按照需求来更改源码,实现自己想要的效果。
Not allowed to load local resource
**的问题,原因是谷歌浏览器禁止直接访问磁盘文件,在实际开发中,文件大多存在服务器中,如果个人想学习测试可以使用在线的pdf资源。/public/pdfplugin/web/viewer.html?file=
,发现怎么也找不到文件,后来去掉了一层/public
,pdf就神奇的出来了。