实现微信小程序和手机app(基于vue)PDF预览功能

引言:最近在做微信小程序和手机端app开发(基于vue),实现一个pdf预览功能。

  • 需求:点击pdf列表,跳转到预览pdf页面,页面上强制阅读10s后,显示一个按钮返回,更新阅读状态;
  • 要求:不能调用本地浏览器预览,不能让用户下载pdf,强制阅读10s后才可返回;
  • 准备:pdf在线文档:“http://cn.createpdfonline.org/pdffiles/test(20211215094117).pdf”

1.微信小程序端

  1. 方式一:webview
<web-view src="http://cn.createpdfonline.org/pdffiles/test(20211215094117).pdf" ></web-view>

运行结果在这里插入图片描述
分析

  • 上述结果是在ios系统上运行的,pdf可以正常显示;
  • 在android系统上,解析失败,一直显示空白界面;
  • 原因:ios可以自动解析pdf,安卓系统则解析不了;
  • 补充:在webview标签上显示按钮,相信做过微信小程序开发的都了解webview标签会默认占满全屏,覆盖掉其他组件,这个时候如果我们想加一个按钮就可以用cover-view标签来实现,具体实现大家可以自己去探索。

总结:使用webview标签在系统上兼容性并不好,不能控制下载状态,而且在阅读状态上只能做到“假控制”。

2.手机app端(vue)

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只有一页,所以在这个基础上进行以下优化:

  • 显示多页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>

结果展示
在这里插入图片描述
总结:在实际测试中发现以下问题

  • 场景:一个pdf列表,点击每个pdf预览;
  • 结果:在ios系统中偶尔会出现白屏的状态,而在android系统中没有这个问题。

经过以上的尝试后,最后决定基于pdf.js来实现业务要求。

引言:经上面的测试,发现了以下问题:

  1. 兼容性不好
  2. 不适用多语言场景(不能共用一套)
  3. 会出现跨域问题
  4. 难于监控阅读状态
  5. 不能控制下载状态

在基于vue开发的移动项目中,如果预览的pdf数量不是很多的话,是可以使用 vue-pdf 的,因为在实际开发测试中,ios系统依次预览多个pdf文件会出现白屏的现象,android系统不会出现。但是此时也很难监控阅读状态,所以最后采用pdf.js来实现业务需求。


目录:

实现效果

1.下载pdfjs

官网:

下载位置

2.放在项目中位置

将下载下来的文件解压,放在项目的public目录下

在这里插入图片描述
为了减小打包体积,bulid文件夹中保留pdf.js和pdf.worker.js即可正常编译。

3.在页面中使用

在我的项目中,pdf文件是以url的形式提供的,我们在页面中放一个iframe,他的src属性等于到viewer.html的相对路径 '/pdfplugin/web/viewer.html?file=' ,file后拼接你的pdf的url就可以了。

代码实现:

<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真的是很强大,我们可以按照需求来更改源码,实现自己想要的效果。

4.出现问题

  • 跨域问题:可参考这篇文章,推荐让后端小伙伴添加配置。
  • 当访问本地文件时,有可能会出现**Not allowed to load local resource**的问题,原因是谷歌浏览器禁止直接访问磁盘文件,在实际开发中,文件大多存在服务器中,如果个人想学习测试可以使用在线的pdf资源。
  • 在使用viewer.html相对路径时有可能找不到正确路径,我之前的路径是/public/pdfplugin/web/viewer.html?file=,发现怎么也找不到文件,后来去掉了一层/public,pdf就神奇的出来了。

5.更改源码(持续更新中)

  • 9
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

失忆症患者_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值