当前位置: 首页 > news >正文

实现 Nuxt3 预览PDF文件

  1. 安装必要的库,这里使用PDF.js库
    npm install pdfjs-dist --save
  2. 为了解决跨域问题,在server/api 下 创建一个请求api, downloadFileByProxy.ts
     
    import { defineEventHandler } from 'h3';export default defineEventHandler(async event => {const { filePath } =  getQuery(event);let matches = filePath?.match(/^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i);let domain = matches && matches[1]; return proxyRequest(event,`https://${domain}/`, {fetch: ()=>fetch(filePath),})
    })
  3. 支持现代浏览器,新建pdfPreviewForMordern.vue组件
    <script setup lang="ts">import { isPdf } from '~/utils/is';import 'pdfjs-dist/web/pdf_viewer.css';import * as pdfjsLib from 'pdfjs-dist';// import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js'; // 旧版浏览器需要换成这个导入import * as pdfjsViewer from 'pdfjs-dist/web/pdf_viewer';import 'pdfjs-dist/build/pdf.worker.entry';import * as pdfjsSandbox from 'pdfjs-dist/build/pdf.sandbox.js';// import {  PDFDocumentProxy } from 'pdfjs-dist/types/src/display/api';import { debounce } from 'lodash-es';const props = defineProps({path: {type: String,default: '',},preview: {type: Boolean,default: true,},});const SANDBOX_BUNDLE_SRC = pdfjsSandbox;pdfjsLib.GlobalWorkerOptions.workerSrc = window.pdfjsWorker;const CMAP_URL = '/pdfjs-dist/cmaps/';const CMAP_PACKED = true;const STANDARD_FONT_DATA_URL = '/pdfjs-dist/standard_fonts/';window.pdfjsLib = pdfjsLib;window.pdfjsViewer = pdfjsViewer;const pdfEventBus = new pdfjsViewer.EventBus();const pdfScriptingManager = new pdfjsViewer.PDFScriptingManager({eventBus: pdfEventBus,sandboxBundleSrc: SANDBOX_BUNDLE_SRC,});const pdfLinkService = new pdfjsViewer.PDFLinkService({eventBus: pdfEventBus,});// (Optionally) enable find controller.const pdfFindController = new pdfjsViewer.PDFFindController({eventBus: pdfEventBus,linkService: pdfLinkService,});let pdfViewer: pdfjsViewer.PDFViewer | null = null;let pdfDocument: PDFDocumentProxy | null = null;const loading = ref<boolean>(true);const visible = ref<boolean>(false);const setVisible = (value: boolean): void => {if (!props.preview) {return;}visible.value = value;};let oldPath = '';const random = ref(Math.floor(Math.random() * 10001));const bufferCache = ref(null); // 使用缓存避免多次请求,可以试具体情况优化与否watch(() => props.path,async (val) => {if (!val || !isPdf(val)) {return;}setTimeout(() => {debounceRenderHandle();}, 500);},{immediate: true,},);const debounceRenderHandle = debounce(() => {initPage(props.path, `pdfjs-container-${random.value}`, 'page-height');}, 500);const preview = async () => {setVisible(true);if (oldPath === props.path) {return;}if (!props.path) {return;}oldPath = props.path;setTimeout(() => {initPage(props.path, `pdfjs-modal-container-${random.value}`);}, 500);};async function getFile(pdfPath: string) {// 为了防止跨域需要再次请求const { _data } = await $fetch.raw(`/api/downloadFileByProxy`,{method: 'get',params: {// filePath: val.split('/').pop(),filePath: pdfPath,},})let blob = _data;let buffer = await blob?.arrayBuffer();return buffer;}async function initPage(pdfPath: string, domId: string, zoom?: string | number) {if (!pdfPath) return;try {// download pdf from api to prevent CORSbufferCache.value = bufferCache.value || (await getFile(pdfPath));let container = document.getElementById(domId);pdfDocument = await pdfjsLib.getDocument({// url: pdfUrl as unknown as URL,data: useCloneDeep(bufferCache.value),cMapUrl: CMAP_URL,cMapPacked: CMAP_PACKED,standardFontDataUrl: STANDARD_FONT_DATA_URL,}).promise;pdfViewer = new pdfjsViewer.PDFViewer({container: container as unknown as HTMLDivElement,eventBus: pdfEventBus,annotationMode: 0,annotationEditorMode: 0,scriptingManager: pdfScriptingManager,linkService: pdfLinkService,});pdfScriptingManager.setDocument(pdfDocument);pdfScriptingManager.setViewer(pdfViewer);pdfLinkService.setDocument(pdfDocument);pdfLinkService.setViewer(pdfViewer);pdfViewer.setDocument(pdfDocument);pdfEventBus.on('pagesinit', () => {if (pdfViewer) {loading.value = false;// TODO: this code will report error, but not affect results: [offsetParent is not set -- cannot scroll]zoom ? pdfLinkService.setHash(`zoom=${zoom}`) : pdfLinkService.setHash(`zoom=100`);}});} catch {// Init pdf Page error}}
    </script><template><div class="w-full h-full"><div @click="preview" class="absolute inset-0 cursor-pointer z-[100]" v-if="preview"></div><img :src="useRuntimeConfig().public.loadingPicture" class="absolute object-cover w-full h-full" v-if="loading" /><div :id="`pdfjs-container-${random}`" class="page-container page-thumbnail-container no-scrollbar"><div :id="`pdfViewer-${random}`" class="pdfViewer pdf-thumbnail-viewer"></div></div><a-modal v-model:visible="visible" :footer="null" width="100%" wrap-class-name="ant-full-modal"><template #closeIcon><span class="font-semibold bg-white cursor-pointer text-litepie-primary-600 text-[16px]"><ms-icon path="close-icon" type="svg" :w="48" :h="48" /></span></template><div :id="`pdfjs-modal-container-${random}`" class="page-container page-modal-container"><div :id="`pdfModalViewer-${random}`" class="pdfViewer"></div></div></a-modal></div>
    </template><style lang="less">.ant-full-modal {.ant-modal {max-width: 100%;top: 0;padding-bottom: 0;margin: 0;}.ant-modal-content {display: flex;flex-direction: column;height: calc(100vh);}.ant-modal-body {flex: 1;padding: 0;}.ant-modal-close {top: 30px;right: 30px;}}
    </style>
    <style lang="less" scoped>.page-container {position: absolute;inset: 0;width: 100%;height: 100%;overflow: auto;}/* another way to scale pdf, still will report error :deep(.pdf-thumbnail-viewer) {--scale-factor: 0.5 !important;canvas {width: 100% !important;height: 100% !important;}}*/
    </style>
    
  4. 对于旧版浏览器,新建pdfPreviewForOld.vue,唯一不同的地方是需要替换pdfjsLib导入
    import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js';
  5. 新建pdfPreview.vue,导入两个组件
    <script setup lang="ts">const supportedOlderBrowser = computed(() => {return getChromeVersion() <= 88 || isSafari();});
    </script><template><div><!-- don't change v-if order, otherwise will report error --><pdfPreviewForOld v-bind="$attrs" v-if="supportedOlderBrowser"></pdfPreviewForOld><pdfPreviewForMordern v-else v-bind="$attrs"></pdfPreviewForMordern></div>
    </template>
  6. 上面用到的判断浏览器的方法
    /*** Determine whether it is safari browser* @return {Boolean} true,false*/
    export const isSafari = () => getUserAgent().indexOf('safari') > -1 && !isChrome(); /*** Determine whether it is chrome browser* @return {Boolean} true,false*/
    export const isChrome = () => /chrome/.test(getUserAgent()) && !/chromium/.test(getUserAgent());export const getChromeVersion = () =>{  let raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);return raw ? parseInt(raw[2], 10) : false;
    }
  7. 导入后预览pdf文件
    <pdf-preview
    :path="picture.originalUrl"
    >
    </pdf-preview>

待优化的问题:

  1. 为了兼容需要重复写两个组件,试过动态导入的方式行不通
  2. 控制台会报[offsetParent is not set -- cannot scroll]的错误,但是不影响预览
     
http://www.lryc.cn/news/480141.html

相关文章:

  • udp为什么会比tcp 有更低的延迟
  • 基于java+SpringBoot+Vue的洗衣店订单管理系统设计与实现
  • HarmonyOS-消息推送
  • 数据分析:宏基因组DESeq2差异分析筛选差异物种
  • 出海企业如何借助云计算平台实现多区域部署?
  • 硬件---1电路设计安全要点以及欧姆定律
  • Linux如何更优质调节系统性能
  • 第三十五章 Vue路由进阶之声明式导航(跳转传参)
  • python爬虫自动库DrissionPage保存网页快照mhtml/pdf/全局截图/打印机另存pdf
  • 基于毫米波雷达和TinyML的车内检测、定位与分类
  • 小E的射击训练
  • React的概念以及发展前景如何?
  • PDF生成:全面解析,C# 如何使用iTextSharp库(或其他类似库)生成PDF文档,包括如何将位图图像嵌入PDF中。
  • 如何选择最适合的消息队列?详解 Kafka、RocketMQ、RabbitMQ 的使用场景
  • gitlab项目如何修改主分支main为master,以及可能遇到的问题
  • RRF(Reciprocal Rank Fusion,倒数排序融合)
  • 移动开发(七):.NET MAUI使用RESTAPI实现查询天气笔记
  • 企业数据无缝对接:从旺店通到金蝶云的入库单管理案例
  • 青少年编程与数学 02-003 Go语言网络编程 19课题、Go语言Restful编程
  • 系统架构设计师论文:论区块链技术及应用
  • 放电电阻是什么
  • 项目模块十七:HttpServer模块
  • Spire.PDF for .NET【页面设置】演示:获取 PDF 文件中的页数
  • 火语言RPA流程组件介绍--点击软件元素
  • c++程序设计速学笔记2基础数据结构
  • 搜维尔科技:SenseGlove案例-利用VR触觉技术培训机组人员
  • OpenCV视觉分析之目标跟踪(10)估计两个点集之间的刚性变换函数estimateRigidTransform的使用
  • Python 虚拟环境创建
  • STL-list容器的使用
  • java中线程与集合的面试题