nuxt使用pdfjs-dist插件实现pdf预览

首先声明一下,pdfjs-dist要是版本安装不对,会出现各种各样的bug!!!

目前我项目中pdfjs-dist的版本和nuxt的版本:

"nuxt": "^2.15.8",
"pdfjs-dist": "2.3.200",

我项目中版本错误遇到的错误:PDF.js报错workerSrc修复

一、项目背景

因为公司的项目中要做合同签订的功能,需要涉及到用户预览合同的功能,因此我的痛苦就到来了o(╥﹏╥)o

这个功能最初讨论之后是由后端返回pdf的链接,前端同学实现pdf在线预览的效果,效果图如下:

 二、功能实现

最初我在网上经过查找决定使用vue-pdf的插件来实现pdf预览的功能,在本地开发中实现了功能,发布到测试环境也运行正常。

最最让人担心的事情还是发生了,发布到正式环境之后整个项目都报错了。我真是吐血了。。。。

Uncaught DOMException: Failed to construct 'Worker': 
Script at 'http://a.com/ef3086d432cfbec65966.worker.js' 
cannot be accessed from origin 'http:/b.com'.

经过一番查找终于找到了具体原因,是因为vue-pdf里面为了加快pdf的渲染使用了Worker ,因为我们项目在正式环境当中静态文件之类的是放在七牛上面的,然而worker 是不能跨域的,将页面和静态资源分开的场景,就会出现跨域问题。--解决 vue-pdf 打包后跨域报错Web Workers 资源跨域问题

三、解决方案

node_modules/vue-pdf/src/vuePdfNoSss.vue 中,有一个 Worker 支持的判断,一般的vue项目当中可以直接注释掉重新打包就可以了。

// 注释if内的两句代码或者注释整个if语句
if ( typeof window !== 'undefined' && 'Worker' in window && navigator.appVersion.indexOf('MSIE 10') === -1 ) {
  // var PdfjsWorker = require('worker-loader!pdfjs-dist/build/pdf.worker.js');
  // PDFJS.GlobalWorkerOptions.workerPort = new PdfjsWorker();
}

注释掉上面的代码,就不走 web worker 了,页面能够正常运行了;相对的,页面加载时间可能会变长,性能可能会降低;

注意:因为我的项目使用了自动化部署,每次部署的时候都会重新npm i拉取新的包,这样的话我在本地修改vue-pdf的文件就不行了

基于以上原因,我只能更换插件重新寻找合适的插件。经过一番查找更换为pdfjs-dist插件,具体实现:

<template>
  <div class="pdf-preview-container">
    <div
      v-for="page in docPages"
      :key="page"
      ref="container"
      class="page-container"
      :style="{
        height: `${pageHeight}px`,
      }"
    >
      <canvas v-if="renderList.includes(page)"> </canvas>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    url: {
      type: String,
      required: true,
    },
    renderPages: {
      type: Number,
      default: 5,
    },
    customScroll: {
      type: Boolean,
      default: false,
    },
    offsetHeight: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      doc: null,
      docPages: 0,
      currentPage: 0,
      pageHeight: 0,
      renderList: [],
    }
  },
  watch: {
    url: {
      immediate: true,
      handler() {
        this.getPDFFile()
      },
    },
  },
  mounted() {
    if (!this.customScroll) {
      document.addEventListener('scroll', this.scroll)
    }
  },
  beforeDestroy() {
    document.removeEventListener('scroll', this.scroll)
  },
  methods: {
    getPDFFile() {
      if (!this.url) return
      this.currentPage = 0
      const pdfJS = require('pdfjs-dist/build/pdf')
      const pdfjsWorker = require('pdfjs-dist/build/pdf.worker.entry')
      pdfJS.GlobalWorkerOptions.workerSrc = pdfjsWorker
      pdfJS.getDocument(this.url).then(pdf => {
        // console.log('pdf: ', pdf)
        this.doc = pdf
        this.docPages = pdf._pdfInfo.numPages
        this.$nextTick(() => {
          this.docPages && this.scrollToPage(1)
        })
      })
    },
    scrollToPage(pageNo) {
      if (this.currentPage === pageNo) return
      this.currentPage = pageNo
      let list = []
      for (
        let page = pageNo - this.renderPages;
        page <= pageNo + this.renderPages;
        page++
      ) {
        list.push(page)
      }
      list = list.filter(page => page <= this.docPages && page >= 1)
      this.$nextTick(() => {
        this.renderList = list
        this.renderList.forEach(page => {
          this.renderPage(page)
        })
      })
    },
    // 渲染page
    renderPage(pageNo) {
      this.doc.getPage(pageNo).then(page => {
        // console.log('page: ', page)
        const container = this.$refs.container[pageNo - 1]
        if (!container) return
        const canvas = container.querySelector('canvas')
        if (!canvas || canvas.__rendered) return
        const ctx = canvas.getContext('2d')
        const dpr = window.devicePixelRatio || 1
        const bsr =
          ctx.webkitBackingStorePixelRatio ||
          ctx.mozBackingStorePixelRatio ||
          ctx.msBackingStorePixelRatio ||
          ctx.oBackingStorePixelRatio ||
          ctx.backingStorePixelRatio ||
          1
        const ratio = dpr / bsr
        const rect = container.getBoundingClientRect()
        const viewport = page.getViewport(1)
        const width = rect.width
        const height = (width / viewport.width) * viewport.height
        canvas.style.width = `${width}px`
        canvas.style.height = `${height}px`
        this.pageHeight = height
        canvas.height = height * ratio
        canvas.width = width * ratio
        ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
        page.render({
          canvasContext: ctx,
          viewport: page.getViewport(width / viewport.width),
        })
        canvas.__rendered = true
      })
    },
    scroll() {
      this.checkRender(document.documentElement)
    },
    checkRender(el) {
      if (!this.pageHeight) return
      let scrollTop = el.scrollTop
      if (el === document.documentElement) {
        scrollTop =
          el.scrollTop || window.pageYOffset || document.body.scrollTop
      }
      let page = Math.floor((scrollTop - this.offsetHeight) / this.pageHeight)
      page = Math.max(page, 1)
      page = Math.min(page, this.docPages)
      this.scrollToPage(page)
    },
  },
}
</script>

页面上调用:

<template>
  <div id="app">
    <pdf-preview :url="url"></pdf-preview>
  </div>
</template>

<script>
import pdfPreview from "./components/PdfPreview.vue";

export default {
  name: "app",
  components: {
    pdfPreview,
  },
  data() {
    return {
      url: "/static/pdf.pdf",
    };
  },
};
</script>

参考链接:https://github.com/Lushenggang/pdf-preview

经过一番折腾终于解决了这个问题,真是吐血了。

原文地址:https://www.cnblogs.com/ziyoublog/p/15502909.html