nuxt使用pdfjs-dist外掛實現pdf預覽
阿新 • • 發佈:2021-11-03
首先宣告一下,pdfjs-dist要是版本安裝不對,會出現各種各樣的bug!!!
目前我專案中pdfjs-dist的版本和nuxt的版本:
"nuxt": "^2.15.8",
"pdfjs-dist": "2.3.200",
一、專案背景
因為公司的專案中要做合同簽訂的功能,需要涉及到使用者預覽合同的功能,因此我的痛苦就到來了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 打包後跨域報錯
三、解決方案
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
經過一番折騰終於解決了這個問題,真是吐血了。