代码改变世界

深入解析:预览pdf(url格式和blob格式)

2025-10-06 22:57  tlnshuju  阅读(0)  评论(0)    收藏  举报
<template>
    <div class="pdf-container">
  <div v-if="loading" class="loading-state"><a-spin size="large" /></div>
    <div v-else-if="error" class="loading-state">
    加载失败,请关闭弹窗重新加载!
  </div>
    <div v-else class="pdf-viewer">
    <pdf
      v-for="i in numPages"
      :key="`${pdfInstanceKey}-${i}`"
      :src="pdfInstance"
      :page="i"
      class="pdf-page"
    />
  </div>
</div>
</template>
<script>
import pdf from 'vue-pdf'
;
import {
debounce
}
from 'lodash-es'
;
export
default {
name: "PdfViewer"
,
components: {
pdf
}
,
props: {
currentPdfUrl: {
type: [String, Object]
, required: true
}
,
fileType: {
type: Number,
default: 1
}
}
,
data(
) {
return {
numPages: 0
,
pdfInstance:
null
,
pdfInstanceKey: 0
,
loading: false
,
error: false
,
activeLoadingTask:
null
,
currentBlobUrl:
null
}
;
}
,
watch: {
currentPdfUrl: {
immediate: true
,
deep: true
,
handler: debounce(
function(newVal
) {
if (newVal)
this.loadPdf(newVal)
;
}
, 300
)
}
}
,
methods: {
async loadPdf(source
) {
try {
this.loading = true
;
this.error = false
;
// 彻底清理前一个PDF
await
this.cleanupPreviousPdf(
)
;
// 准备新的PDF源
const pdfSource =
this.fileType === 1
? {
url: source, withCredentials: false
}
:
this.createBlobUrl(source)
;
// 创建加载任务
this.activeLoadingTask =
this.fileType === 1
? pdf.createLoadingTask({
url: source,
withCredentials: false
,
cMapUrl: '\'@/assets/cmaps/\''
,
// 'https://fastly.jsdelivr.net/npm/pdfjs-dist@2.11.338/cmaps/',
cMapPacked: true
}
)
: pdf.createLoadingTask(
this.createBlobUrl(source)
)
;
this.pdfInstance =
this.activeLoadingTask;
// 监听加载完成
const pdfDocument =
await
this.activeLoadingTask.promise;
this.numPages = pdfDocument.numPages;
// 成功加载后增加实例key
this.pdfInstanceKey++
;
}
catch (err) {
console.error('PDF加载失败:'
, err)
;
this.handleLoadError(err)
;
}
finally {
this.loading = false
;
}
}
,
createBlobUrl(fileObj
) {
// 释放之前的Blob URL
if (
this.currentBlobUrl) {
URL.revokeObjectURL(
this.currentBlobUrl)
;
}
this.currentBlobUrl = URL.createObjectURL(fileObj.originFileObj)
;
return
this.currentBlobUrl;
}
,
async cleanupPreviousPdf(
) {
// 清理加载任务
if (
this.activeLoadingTask) {
try {
// 先取消可能存在的promise
if (
this.activeLoadingTask._transport &&
this.activeLoadingTask._transport.destroy) {
this.activeLoadingTask._transport.destroy(
)
;
}
// 销毁worker
this.activeLoadingTask.destroy(
)
;
}
catch (e) {
console.warn('清理PDF worker时出错:'
, e)
;
}
this.activeLoadingTask =
null
;
}
// 重置状态
this.pdfInstance =
null
;
this.numPages = 0
;
}
,
handleLoadError(error
) {
this.error = true
;
this.numPages = 0
;
// 特殊处理常见错误
if (error.name === 'PasswordException'
) {
console.warn('PDF需要密码'
)
;
}
else
if (error.name === 'InvalidPDFException'
) {
console.warn('无效的PDF文件'
)
;
}
}
,
retryLoading(
) {
this.loadPdf(
this.currentPdfUrl).catch((
)=>
{
}
)
;
}
}
,
beforeDestroy(
) {
this.cleanupPreviousPdf(
)
;
if (
this.currentBlobUrl) {
URL.revokeObjectURL(
this.currentBlobUrl)
;
}
}
}
;
</script>
<style scoped lang="less">
.pdf-container {
width: 100%;
//height: 100%;
overflow: auto;
background-color: #f0f0f0;
.pdf-viewer {
display: flex;
flex-direction: column;
align-items: center;
.pdf-page {
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15)
;
background-color: white;
width: 100%;
&:last-child {
margin-bottom: 0;
}
}
}
}
.loading-state{
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
</style>