Vue2項(xiàng)目如何使用pdfjs-dist解析pdf
先說(shuō)結(jié)論
使用pdfjs-dist的2.7.570版本
使用pdfjs-dist的2.7.570版本的es5產(chǎn)物,該版本的es5的build產(chǎn)物沒(méi)有特殊寫法,對(duì)vue.config.js、babel.config.js的兼容性最好,不需要額外再下載其他插件,比如可選鏈插件等
引用方式
import * as pdfjsLib from 'pdfjs-dist/es5/build/pdf' import pdfWorker from 'pdfjs-dist/es5/build/pdf.worker.entry' pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker pdfjsLib.GlobalWorkerOptions.isEvalSupported = false // 關(guān)閉 eval 支持(防止漏洞)
背景
某天,產(chǎn)品同事發(fā)現(xiàn)C端的資質(zhì)展示頁(yè)面(H5)的圖片渲染有問(wèn)題,經(jīng)排查發(fā)現(xiàn)有些鏈接是.pdf結(jié)尾的pdf文件,最后導(dǎo)致某些機(jī)型無(wú)法正常渲染。如下圖所示
這里有個(gè)小點(diǎn): ios機(jī)型會(huì)把pdf渲染出來(lái)1頁(yè),安卓機(jī)型無(wú)法渲染pdf
遇到問(wèn)題時(shí)的思考
這種情況在線上是否多,如果量級(jí)不多,個(gè)人感覺(jué)可以嘗試后端進(jìn)行解析PDF轉(zhuǎn)成圖片,在更新數(shù)據(jù)庫(kù)。但這需要結(jié)合業(yè)務(wù)系統(tǒng)來(lái)看,因?yàn)檫@是“資質(zhì)文件”,所以得保證業(yè)務(wù)系統(tǒng)在這方面的功能是怎么樣的,是只能上傳圖片還是pdf,還是都能,不過(guò)這是后面分析才得到的結(jié)果
后端是否好解決,這需要和后端溝通
需求
能夠根據(jù)pdf鏈接展示出他的所有頁(yè)數(shù)內(nèi)容,不失真,能在各個(gè)機(jī)型的web-view中運(yùn)行,如安卓app、ios的app、支付小程序、微信小程序。
只做pdf解析預(yù)覽功能,不需要額外功能,把內(nèi)容轉(zhuǎn)成圖片即可。
盡量做到不修改vue.config.js、babel.config.js來(lái)解決這個(gè)問(wèn)題,理想情況就是新增一個(gè)組件,然后對(duì)應(yīng)頁(yè)面判斷是鏈接,最后使用下就好了。畢竟這是老項(xiàng)目,這些可不能亂動(dòng)。
前期開發(fā)工作
與后端溝通,結(jié)果是后端不好解決,只能交給前端來(lái)解決
先問(wèn)gpt,具體方案和關(guān)鍵依賴,可能的坑點(diǎn)
搜索gpt,根據(jù)得到的關(guān)鍵依賴(pdfjs-dist),了解他的issue、坑點(diǎn)、demo等
順便搜下相關(guān)文章,但發(fā)現(xiàn)質(zhì)量都挺一般的,數(shù)量有點(diǎn)少,于是就參考gpt的,其次就是pdfjs-dist的文檔有點(diǎn)難看懂
具體實(shí)施
下載 pdfjs-dist,下載時(shí)下的是最新版本,5.x.x
新建pdf-viewr.vue組件
復(fù)制gpt給我的代碼
運(yùn)行項(xiàng)目
# gpt給的案例 import * as pdfjsLib from 'pdfjs-dist/build/pdf' import pdfWorker from 'pdfjs-dist/build/pdf.worker.entry' pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker // 解析過(guò)程省略,主要核心是引用方式的問(wèn)題
踩坑之旅
依照上述實(shí)施行為,結(jié)果就是直接報(bào)錯(cuò),運(yùn)行不了,于是我就開始了我的漫長(zhǎng)踩坑之旅
我的依賴&版本
- node: 12.22.22
- @vue/cli: ^3.12.0
- babel-core: 7.0.0-bridge.0
- vue: 2.6.x
版本問(wèn)題
使用pdfjs-dist遇到了非常多的版本兼容性問(wèn)題,由于用的是最新版本,有很多寫法,和我的對(duì)不上,于是我的想法是降版本,但一開始不知道降到多少,畢竟有1549個(gè)版本,想法是問(wèn)ai,但ai回答出來(lái)的版本還是不太行,于是我就打算換個(gè)方式,找找有沒(méi)有現(xiàn)成的封裝好的組件
這里找到的是vue-pdf,這個(gè)組件是用pdfjs-dist+vue2的,我大喜過(guò)望,馬上下載使用,寫起來(lái)很快呀,但新的問(wèn)題已然埋下
vue-pdf問(wèn)題
- issue數(shù)量特別多,只能說(shuō)慎用,一共235個(gè)
- 這個(gè)組件在打包上線后,會(huì)無(wú)法正常加載出來(lái),導(dǎo)致對(duì)應(yīng)頁(yè)面白屏。根據(jù)gpt的說(shuō)法是可能丟失了pdfjs-dist,然后我就去找原因,這種情況先找issue,我在vue-pdf的issue中看到了有人遇到了同樣的問(wèn)題,然后我在某個(gè)issue中,看到了這么一句話:使用2.7.570版本。
于是我順藤摸瓜,找到pdfjs-dist的版本記錄,找到他的內(nèi)容是什么,然后我就發(fā)現(xiàn)了一個(gè)讓我很興奮的點(diǎn):
這個(gè)依賴的有build和es5的build產(chǎn)物?。?!,這是最關(guān)鍵的,他的其他版本我看了幾個(gè),我發(fā)現(xiàn)沒(méi)有es5的打包產(chǎn)物

用build的產(chǎn)物仍然存在特殊寫法,比如可選鏈,因?yàn)槲业捻?xiàng)目沒(méi)有可選鏈的babel插件,所以仍然不能用
但是es5的產(chǎn)物沒(méi)有特殊寫法,所以理論上用這個(gè)就能解決了,因?yàn)橐脠?bào)錯(cuò)的問(wèn)題是語(yǔ)法相關(guān)的兼容性問(wèn)題,我又不想新增babel插件、loader依賴等
最后在我使用了這個(gè)版本后,修改了原先AI提供的案例中的引用方式,于是項(xiàng)目正常跑了起來(lái),這里就不使用vue-pdf了,使用pdfjs-dist,用它來(lái)解析,然后轉(zhuǎn)圖片。
對(duì)比如下:
# 前 import * as pdfjsLib from 'pdfjs-dist/build/pdf' import pdfWorker from 'pdfjs-dist/build/pdf.worker.entry' pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker # 后 import * as pdfjsLib from 'pdfjs-dist/es5/build/pdf' import pdfWorker from 'pdfjs-dist/es5/build/pdf.worker.entry' pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker
病毒問(wèn)題
病毒問(wèn)題也是看vue-pdf的issue才知道的
根據(jù)報(bào)告鏈接所提供的方案就是
pdfjsLib.GlobalWorkerOptions.isEvalSupported = false // 關(guān)閉 eval 支持(防止漏洞)
小結(jié)
為了解決pdfjs-dist在vue2的老項(xiàng)目的兼容性問(wèn)題,最終方案就是使用pdfjs-dist@2.7.570,這樣就不用影響到之前的vue.config.js、babel.config.js相關(guān)配置了。
這個(gè)過(guò)程解決兼容問(wèn)題,費(fèi)時(shí)費(fèi)力,還以為無(wú)法解決了,不過(guò)皇天不負(fù)苦心人,這個(gè)問(wèn)題還是被解決了,所以記錄下。
后續(xù)計(jì)劃
自己打包高版本(5.x.x)的pdfjs-dist來(lái)適應(yīng)內(nèi)部項(xiàng)目
研究vue-pdf打包上線白屏問(wèn)題
接入vue3項(xiàng)目
分頁(yè)加載優(yōu)化,大文件優(yōu)化
代碼
pdf解析預(yù)覽組件
<template>
<div class="pdf-viewer">
<van-loading v-if="loading" type="spinner" size="32px" vertical>加載中</van-loading>
<div v-else>
<div v-if="error" class="fallback">
<p>當(dāng)前信息無(wú)法加載,您可以<a :href="pdfUrl" target="_blank">點(diǎn)擊查看</a></p>
</div>
<div v-else>
<div v-for="(page, index) in pages" :key="index" class="pdf-page">
<img :src="page" :alt="'pdf ' + (index + 1)" class="pdf-image">
</div>
</div>
</div>
</div>
</template>
<script>
import { Loading } from 'vant'
// ? 使用pdfjs-dist的2.7.570版本的es5產(chǎn)物,該版本的es5的build產(chǎn)物沒(méi)有特殊寫法,對(duì)本項(xiàng)目vue.config.js、babel.config.js的兼容性最好
import * as pdfjsLib from 'pdfjs-dist/es5/build/pdf'
import pdfWorker from 'pdfjs-dist/es5/build/pdf.worker.entry'
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker
pdfjsLib.GlobalWorkerOptions.isEvalSupported = false // 關(guān)閉 eval 支持(防止漏洞)// 漏洞鏈接: https://www.venustech.com.cn/new_type/aqtg/20240514/27492.html
export default {
name: 'pdf-viewer',
props: {
pdfUrl: {
type: String,
required: true,
},
},
components: {
[Loading.name]: Loading,
},
data() {
return {
pages: [],
loading: true,
error: false,
}
},
mounted() {
this.loadPdf()
},
methods: {
async loadPdf() {
try {
const loadingTask = pdfjsLib.getDocument(this.pdfUrl)
const pdf = await loadingTask.promise
const pageImages = []
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
const page = await pdf.getPage(pageNum)
const viewport = page.getViewport({ scale: 2 }) // scale 調(diào)大可提高清晰度
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
canvas.width = viewport.width
canvas.height = viewport.height
await page.render({ canvasContext: context, viewport }).promise
pageImages.push(canvas.toDataURL())
}
this.pages = pageImages
this.loading = false
} catch (e) {
console.error('PDF 加載失敗:', e)
this.error = true
this.loading = false
}
},
},
}
</script>
<style lang="stylus" scoped>
.pdf-viewer {
width: 100%;
overflow-x: hidden;
}
.pdf-image {
width: 100%;
display: block;
object-fit: contain;
}
.fallback {
text-align: center;
color: #999;
font-size: 14px;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
a {
color: #007aff;
text-decoration: underline;
}
}
</style>鏈接類型判斷
// 判斷文件類型(擴(kuò)展名優(yōu)先,fallback 為 HEAD)
async detectFileType(url) {
// 1. 特殊 scheme 優(yōu)先判斷
if (url.startsWith('data:image/')) return 'image'
if (url.startsWith('data:application/pdf')) return 'pdf'
if (url.startsWith('blob:')) return 'unknown'
// 2. 擴(kuò)展名判斷(寬松匹配)
if (/\.(jpe?g|png|gif|bmp|webp|svg)([\?#].*)?$/i.test(url)) return 'image'
if (/\.pdf([\?#].*)?$/i.test(url)) return 'pdf'
// 3. 嘗試發(fā) HEAD 請(qǐng)求獲取 content-type
try {
const res = await axios.head(url) // ? 支付寶小程序如果遇到404鏈接會(huì)導(dǎo)致頁(yè)面白屏,微信、安卓不會(huì)
const contentType = res.headers['content-type'] || ''
if (contentType.includes('image/')) return 'image'
if (contentType.includes('application/pdf')) return 'pdf'
} catch (e) {
console.warn('鏈接類型獲取失敗:', url, e.message)
}
return 'unknown'
}
到此這篇關(guān)于Vue2項(xiàng)目如何使用pdfjs-dist解析pdf的文章就介紹到這了,更多相關(guān)Vue2解析pdf內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue項(xiàng)目如何引入json數(shù)據(jù)
這篇文章主要介紹了vue項(xiàng)目如何引入json數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
vue登錄頁(yè)面回車執(zhí)行事件@keyup.enter.native問(wèn)題
這篇文章主要介紹了vue登錄頁(yè)面回車執(zhí)行事件@keyup.enter.native問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03
vue之el-tree懶加載數(shù)據(jù)并且實(shí)現(xiàn)樹的過(guò)濾問(wèn)題
這篇文章主要介紹了vue之el-tree懶加載數(shù)據(jù)并且實(shí)現(xiàn)樹的過(guò)濾問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
vue3中g(shù)etCurrentInstance不推薦使用及在<script?setup>中獲取全局內(nèi)容的三種方式
這篇文章主要給大家介紹了關(guān)于vue3中g(shù)etCurrentInstance不推薦使用及在<script?setup>中獲取全局內(nèi)容的三種方式,文中通過(guò)介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-02-02

