当前位置: 首页 > 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]的错误,但是不影响预览
     

相关文章:

实现 Nuxt3 预览PDF文件

安装必要的库&#xff0c;这里使用PDF.js库 npm install pdfjs-dist --save 为了解决跨域问题&#xff0c;在server/api 下 创建一个请求api&#xff0c; downloadFileByProxy.ts import { defineEventHandler } from h3;export default defineEventHandler(async event >…...

udp为什么会比tcp 有更低的延迟

UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;相比TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff09;具有更低的延迟&#xff0c;这主要归因于UDP协议的设计特点和机制。以下是对UDP比TCP延迟低的原因的详细…...

基于java+SpringBoot+Vue的洗衣店订单管理系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…...

HarmonyOS-消息推送

一. 服务简述 Push Kit&#xff08;推送服务&#xff09;是华为提供的消息推送平台&#xff0c;建立了从云端到终端的消息推送通道。所有HarmonyOS 应用可通过集成 Push Kit&#xff0c;实现向应用实时推送消息&#xff0c;使消息易见&#xff0c;构筑良好的用户关系&#xff0…...

数据分析:宏基因组DESeq2差异分析筛选差异物种

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍原理:计算步骤:结果:加载R包准备画图主题数据链接导入数据Differential abundance (No BP vs 2BP TA)构建`countData`矩阵过滤低丰度物种构建DESeq数据对象DESeq2差异分析画图Di…...

出海企业如何借助云计算平台实现多区域部署?

云计算de小白 如需进一步了解&#xff0c;请单击链接了解有关 Akamai 云计算的更多信息 在本文中我们将告诉大家如何在Linode云计算平台上借助VLAN快速实现多地域部署。 首先我们需要明确一些基本概念和思想&#xff1a; 部署多区域 VLAN 为了在多区域部署中在不同的 VLAN …...

硬件---1电路设计安全要点以及欧姆定律

前言&#xff1a; 一直搞的东西都偏软件&#xff0c;硬件也一直在学&#xff0c;元器件、基础电路知识、PCB设计、模电运放都学的马马虎虎&#xff0c;因此决定进行系统性学习&#xff0c;内容基本来源于手里的视频和书本以及自己的感悟。 一电路安全 1电路安全 在初期基础…...

Linux如何更优质调节系统性能

一、硬件优化 增加物理内存&#xff1a;最直接的提升系统性能的方法。内存不足时&#xff0c;系统会频繁进行交换&#xff08;swapping&#xff09;活动&#xff0c;这会显著降低系统的响应速度&#xff0c;因为磁盘IO速度远低于内存访问速度。通过增加内存&#xff0c;可以减…...

第三十五章 Vue路由进阶之声明式导航(跳转传参)

目录 一、引言 二、查询参数传参 2.1. 使用方式 2.2. 完整代码 2.2.1. main.js 2.2.2. App.vue 2.2.3. Search.vue 2.2.4. Home.vue 2.2.5. index.js 三、动态路由传参 3.1. 使用方式 3.2. 完整代码 3.2.1. main.js 3.2.2. App.vue 3.2.3. Search.vue 3.2.4. Hom…...

python爬虫自动库DrissionPage保存网页快照mhtml/pdf/全局截图/打印机另存pdf

目录 零一、保存网页快照的三种方法二、利用打印机保存pdf的方法 零 最近星球有人问如何使用页面打印功能&#xff0c;另存为pdf 一、保存网页快照的三种方法 解决方案已经放在星球内&#xff1a;https://articles.zsxq.com/id_55mr53xahr9a.html当然也可以看如下代码&…...

基于毫米波雷达和TinyML的车内检测、定位与分类

英文标题&#xff1a;In-Cabin Detection, Localization and Classification based on mmWave Radar with TinyML 作者信息&#xff1a; 王志飞&#xff0c;程一格&#xff0c;彭辉&#xff0c;周会强&#xff0c;王铮&#xff0c;刘宏全所属机构&#xff1a;Calterah Semico…...

小E的射击训练

问题描述 小E正在训练场进行射击练习&#xff0c;靶有10个环&#xff0c;靶心位于坐标(0, 0)。每个环对应不同的得分&#xff0c;靶心内&#xff08;半径为1&#xff09;得10分&#xff0c;依次向外的每个环分数减少1分。若射击点在某个半径为i的圆内&#xff0c;则得11-i分。…...

React的概念以及发展前景如何?

React是一个由Facebook开发的用于构建用户界面的的开源JavaScript库&#xff0c;它主要用于构建大型、动态的Web应用程序。React的主要特点是使用VirtualDOM&#xff08;虚拟DOM&#xff09;来优化性能&#xff0c;并使用声明式的编程方式来编写UI。 React的主要概念包括&#…...

PDF生成:全面解析,C# 如何使用iTextSharp库(或其他类似库)生成PDF文档,包括如何将位图图像嵌入PDF中。

一、概述 PDF&#xff08;Portable Document Format&#xff09;是一种广泛使用的文档格式&#xff0c;由Adobe公司在1993年推出。PDF的目标是能够在任何设备上呈现固定格式的文档&#xff0c;无论是在不同的操作系统、硬件设备&#xff0c;还是在打印时&#xff0c;都能保证文…...

如何选择最适合的消息队列?详解 Kafka、RocketMQ、RabbitMQ 的使用场景

引言 在日常开发中&#xff0c;消息队列已经成为业务场景中几乎不可或缺的一部分。无论是订单系统、日志收集、分布式事务&#xff0c;还是大数据实时流处理&#xff0c;消息队列都在支撑着这些关键环节。目前市面上常用的消息队列有三种(ActiveMQ 虽然在企业集成中仍有应用&a…...

gitlab项目如何修改主分支main为master,以及可能遇到的问题

如果你希望将 Git 仓库的主分支名称从 main 修改为 master&#xff1a; 1. 本地修改分支名称 首先&#xff0c;切换到 main 分支&#xff1a; git checkout main将 main 分支重命名为 master&#xff1a; git branch -m main master2. 更新远程仓库 将本地更改推送到远程仓库…...

RRF(Reciprocal Rank Fusion,倒数排序融合)

RRF(Reciprocal Rank Fusion,倒数排序融合) 摘要 倒数排序融合 RRF 是一种简单的方法&#xff0c;用于结合多个 IR(Information Retrieval) 系统的文档排名&#xff0c;始终比任何单独的系统产生更好的结果。 通过使用 RRF 来结合几个TREC实验的结果&#xff0c;并建立一个 …...

移动开发(七):.NET MAUI使用RESTAPI实现查询天气笔记

目录 一、接口准备 二、实体部分 三、页面部分 四、后台代码逻辑 五、总结 在移动开发过程中,第三方对接是非常常见的。今天给大家分享.NET MAUI如何使用REST API实现输入城市名称查询天气的示例,希望对大家学习.NET MAUI可以提供一些帮助! 一、接口准备 首先我们需要…...

企业数据无缝对接:从旺店通到金蝶云的入库单管理案例

【类型:盘盈入库】旺店通-入库单管理>金蝶-其他入库单 在企业的日常运营中&#xff0c;数据的高效集成和准确传递是确保业务顺畅运行的关键。本文将分享一个实际案例&#xff0c;展示如何通过轻易云数据集成平台&#xff0c;将旺店通企业奇门的数据无缝对接到金蝶云星空&am…...

青少年编程与数学 02-003 Go语言网络编程 19课题、Go语言Restful编程

青少年编程与数学 02-003 Go语言网络编程 19课题、Go语言Restful编程 课题摘要:一、微服务微服务的主要特点包括&#xff1a;微服务架构的挑战&#xff1a;微服务的应用场景&#xff1a; 二、RESTfulRESTful的核心原则和特征包括&#xff1a;RESTful API的优势&#xff1a;REST…...

Kafka Connect集群部署踩坑实录:从单机到高可用的完整配置与监控方案

Kafka Connect生产级部署实战&#xff1a;高可用架构设计与监控体系构建 当数据管道成为企业核心基础设施时&#xff0c;Kafka Connect的稳定性直接关系到业务连续性。去年某电商大促期间&#xff0c;因单点故障导致数据同步延迟6小时的教训仍历历在目——这正是我们需要深入探…...

低温预警!固化慢、易开裂……密封胶冬季施工手册

低温预警!固化慢、易开裂……密封胶冬季施工手册 硅酮耐候密封胶主要作用是保障幕墙的气密性、水密性。其出现问题,可能会导致耐候密封失效,从而造成幕墙漏水漏气,影响幕墙的正常使用。耐候密封胶由于考虑到现场施工,几乎都是单组分硅酮密封胶产品。进入冬季,气候变化明…...

PCL2启动器离线登录按钮消失?5分钟快速修复指南

PCL2启动器离线登录按钮消失&#xff1f;5分钟快速修复指南 【免费下载链接】PCL Minecraft 启动器 Plain Craft Launcher&#xff08;PCL&#xff09;。 项目地址: https://gitcode.com/gh_mirrors/pc/PCL 你是否遇到过PCL2启动器离线登录按钮突然消失的困扰&#xff1…...

CircuitPython开发进阶:从库文档解读到内存优化与异步编程实战

1. 从“能用”到“精通”&#xff1a;为什么你需要深入理解CircuitPython库文档刚接触CircuitPython时&#xff0c;我们往往是从复制粘贴示例代码开始的。这没什么问题&#xff0c;快速让一个LED闪烁起来&#xff0c;或者让传感器读出数据&#xff0c;那种即时反馈的成就感是驱…...

基于Panel与LLM构建智能数据可视化应用的架构与实践

1. 项目概述与核心价值最近在数据可视化与交互应用开发领域&#xff0c;一个名为holoviz-topics/panel-chat-examples的项目仓库引起了我的注意。乍一看&#xff0c;这似乎只是将聊天界面&#xff08;Chat Interface&#xff09;与 Panel 这个强大的 Python 交互式仪表盘库结合…...

Nextra:基于Next.js的现代化文档站构建利器

1. 项目概述&#xff1a;为什么Nextra能成为文档站构建的“瑞士军刀”&#xff1f;如果你最近在寻找一个构建技术文档、博客或个人知识库的工具&#xff0c;大概率会听到“Nextra”这个名字。它不是一个独立框架&#xff0c;而是一个基于Next.js的静态站点生成器&#xff0c;专…...

Deep Lake:AI数据湖实战指南,解决深度学习数据管理难题

1. 项目概述&#xff1a;当数据湖遇上深度学习如果你在深度学习项目里被数据管理搞得焦头烂额过&#xff0c;那你肯定懂我在说什么。模型训练到一半&#xff0c;发现数据版本不对&#xff0c;或者想对海量图像、视频做快速查询和采样&#xff0c;结果被IO速度卡得死死的。传统的…...

从零打造开源机械爪:低成本机器人抓取方案全解析

1. 项目概述与核心价值最近在GitHub上看到一个挺有意思的项目&#xff0c;叫“OpenClawTuto”。光看这个名字&#xff0c;你可能会有点摸不着头脑&#xff0c;它不像“XX管理系统”或者“XX深度学习框架”那样一目了然。但作为一个在开源社区和自动化领域摸爬滚打了十来年的老手…...

Cursor编辑器状态快照插件开发:一键保存与恢复工作区

1. 项目概述&#xff1a;一个专为开发者设计的“后悔药”如果你是一名重度使用 Cursor 编辑器的开发者&#xff0c;那么你一定经历过这样的场景&#xff1a;在沉浸式编码时&#xff0c;为了快速定位或修改&#xff0c;你可能会频繁地使用CtrlClick跳转到函数定义&#xff0c;或…...

Linux内存使用分析与泄漏排查

Linux内存使用分析与泄漏排查内存问题往往不像磁盘满那样直观&#xff0c;也不像进程崩溃那样立刻可见。很多服务在内存异常初期仍然可以运行&#xff0c;只是响应逐渐变慢、交换开始活跃、最终被系统回收或触发 OOM。中级 Linux 工程师需要掌握的&#xff0c;不只是看“还剩多…...