最近做到一个功能,PDF预览;这个功能看着蛮简单的,结果搞了两个下午,真是欲哭无泪。记录一下所查到的预览方法。
我在网上找了蛮多教程的,大致都是以下几个方法实现预览:

  1. 使用 iframe 标签
  2. 使用 embed 标签
  3. 安装 Vue 插件 vue-pdf
  4. 安装 Vue 插件 pdfh5

下面配合代码详细说我实现预览的过程吧!

iframe标签
<iframe :src="url" style="border: none;width: 100%;height: 100%;" frameborder="0" />
<script>
export default {
  data() {
    return {
      show: false, 
      url: '', // pdf地址
    }
  },
  methods: {
}
</script>

这个标签其实蛮方便的,毕竟上一页、下一页,跳转,下载都是齐全的,个人觉得如果是用在pc端就非常的舒适,一个标签解决所有,但是如果在移动端可能就有点小毛病;我这次需要在一个弹窗里预览PDF文件,在展示时样式就很丑(如下图),不过我没多挣扎就放弃了

Android开发pdf预览体积小 移动端pdf预览插件_pdf

embed 标签
<embed :src="url" type="application/pdf" width: 100% height: 100% >
<script>
export default {
  data() {
    return {
      show: false, 
      url: '', // pdf地址
    }
  },
  methods: {
}
</script>

这个方法有局限性,如果浏览器不支持PDF嵌入,那这个标签就什么也不展示。

Vue 插件 vue-pdf
npm install --save vue-pdf // 安装一下
<!--这是一次加载完,还可以分页展示,这里就不详细说了-->
<div class="toast">
          <PDF v-for="i in pageNum" :key="i" ref="pdf" :src="url" :page="i" style="width: 100%" />
</div>
<script>
import PDF from 'vue-pdf' // 引入
import { CellGroup, Field, Popup, Toast } from 'vant'
export default {
  components: {
    PDF, [CellGroup.name]: CellGroup, [Field.name]: Field, [Popup.name]: Popup, [Toast.name]: Toast
  },
  data() {
    return {
      show: false, // 打开弹窗
      url: '', // 预览地址
      currentPage: 0, // 页码
      pageNum: null
    }
  },
  computed: {
  },
  created() {
  },
  methods: {
    // 查看pdf
    lookPdf(item) {
      console.log('打开弹窗')
      this.getNumPages()
      this.show = true
    },
    getNumPages() {
      this.url = 'https://xxx/xxxx/test.pdf'
      const loadingTask = PDF.createLoadingTask(this.url)
      loadingTask.promise
        .then((pdf) => {
          this.pageNum = pdf.numPages
        })
        .catch((err) => {
          console.error('pdf 加载失败', err)
        })
    }
  }
}
</script>

这个方法,我之前做PC端的时候,真的很丝滑;但这次做移动端就开始报错,用浏览器调试时,一到移动端模式就开始报错(如下图),在网上找的解决教程也没起作用,果断放弃。毕竟要多给别的方法机会嘛!

Android开发pdf预览体积小 移动端pdf预览插件_h5_02

npm install pdfh5
npm install pdfh5 // 安装一下
<template>
  <div class="m-pdf">
    <div id="pdf-content" />
  </div>
</template>
<script>
import Pdfh5 from 'pdfh5'
import 'pdfh5/css/pdfh5.css'
export default {
  name: 'Pdfh5',
  data() {
    return {
      pdfh5: null,
    }
  },
  mounted() {
  },
  methods: {
    initPdf(pdfUrl) {
      console.log('子组件的地址', pdfUrl)
      // pdfh5实例化时传两个参数:selector选择器,options配置项参数,会返回一个pdfh5实例对象,可以操作pdf,监听相关事件
      this.pdfh5 = new Pdfh5('#pdf-content', {
        pdfurl: pdfUrl
      })
      this.pdfh5.scrollEnable(true) // 允许pdf滚动
      // 监听pdf准备开始渲染,可以拿到pdf总页数
      this.pdfh5.on('ready', function() {
        console.log('总页数:' + this.totalNum)
      })
      // 监听pdf加载完成事件
      this.pdfh5.on('complete', (status, msg, time) => {
        console.log('状态:' + status + ',信息:' + msg + ',耗时:' + time + '毫秒')
      })
    }
  }
}
</script>
// 父组件
<template>
  <button @click="showPdf">
  <div v-if = "show">
      <preview-pdf ref="previewPdfh5" style="height:450px;margin-bottom:12px;" />
   </div>
</template>
<script>
import PreviewPdf from '../pdf.vue'
export default {
components: {
    PreviewPdf
  },
  data() {
    return {
      show: false, 
      url: '', // pdf地址
    }
  },
  methods: {
  showPdf(){
     this.show
     this.url = 'https://xxx/xxxx/test.pdf' // pdf文件的地址
     this.$refs.previewPdfh5.initPdf(this.url) // 父组件调用子组件的方法,动态渲染pdf
  }
}
</script>

这个方法到这里也很丝滑,上述方法的问题都没有碰到;然而在调后端接口时,报错了。

Android开发pdf预览体积小 移动端pdf预览插件_vue.js_03


真是欲哭无泪,我上网找教程,根本没有。接口传过来的PDF里有一些字段是动态变化的,就导致这个问题出现。在我不懈奋斗了几个小时之后,无意间看到了pdfh5的中文文档的一个参数例子(如图),问题就解决啦!

Android开发pdf预览体积小 移动端pdf预览插件_javascript_04


给代码加个参数

methods: {
    initPdf(pdfUrl) {
      console.log('子组件的地址', pdfUrl)
      this.pdfh5 = new Pdfh5('#pdf-content', {
        pdfurl: pdfUrl,
        cMapUrl: 'https://unpkg.com/pdfjs-dist@2.0.943/cmaps/' // 改一下这个参数的默认地址
      })
      this.pdfh5.scrollEnable(true) 
      this.pdfh5.on('ready', function() {
        console.log('总页数:' + this.totalNum)
      })
      this.pdfh5.on('complete', (status, msg, time) => {
        console.log('状态:' + status + ',信息:' + msg + ',耗时:' + time + '毫秒')
      })
    }
  }

这是我参考的文档地址:https://github.com/EncodingAESKey/pdfh5/blob/master/README.md

总结一下:磕磕碰碰也终于实现了功能。这几个方法都能实现功能;如果图方便就用iframe标签,图样式简洁且使用方法简单就用 vue-pdf 插件,图样式简洁且功能齐全就用 pdfh5 插件;至于embed 标签,我还没用过。不过我用 vue-pdf 插件遇到的问题,到现在也没解决好,不知道是不是移动端要添加什么配置,还是我代码环境不兼容;后面如果有好的解决方法,我会记得来更。