前端实现PDF预览的几种选择(pdfjs-dist、react-pdf、pdf-viewer)

devtools/2024/12/27 11:09:58/

记录

PDF预览的选型

对于浏览器自带的PDF预览

如果能直接使用,那自然最好不过了,但考虑多种因素,比如权限问题,禁止用户去下载PDF、预览样式不统一(不同浏览器PDF预览的实现不同),所有最终放弃了该方式

看了很多例子,大部分都是围绕pdf.js这个库展开的,所以我的选项也是围绕它去找的

最终找到几个不错的

接下来我会依次介绍一下三个库的使用

pdfjsdist_18">pdfjs-dist

其实就是pdfjs库,只是对其进行打包发布到npm了

直接根据官方文档的案例对比进行操作就行了

Examples

import { useEffect, useRef } from 'react'
import * as PDFJS from 'pdfjs-dist'PDFJS.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.js`interface Props {fileUrl: string
}const FilePDF = ({ fileUrl }: Props) => {const pdfContainer = useRef<HTMLCanvasElement>(null)const pdfCtx = useRef<CanvasRenderingContext2D | null>(null)const pdfDoc = useRef<any>()const pdfNumPages = useRef(0)// 依次渲染所有页面const renderPage = num => {pdfDoc.current!.getPage(num).then(page => {const viewport = page.getViewport({ scale: 1 })pdfContainer.current!.width = viewport.widthpdfContainer.current!.height = viewport.heightpage.render({viewport,canvasContext: pdfCtx.current!}).promise.then(() => {if (num < pdfNumPages.current) {renderPage(num + 1)}})})}useEffect(() => {pdfCtx.current = pdfContainer.current!.getContext('2d')PDFJS.getDocument(fileUrl).promise.then(pdfDoc_ => {pdfDoc.current = pdfDoc_pdfNumPages.current = pdfDoc_.numPagesrenderPage(1)})}, [])return (<div className={'flex h-full w-full items-center justify-center rounded-lg'}><canvas ref={pdfContainer}></canvas></div>)
}export default FilePDF

这种实现比较繁琐,所以也就有了react-pdf,对pdfjs-dist进行了一层封装

效果展示

在这里插入图片描述

pdf_88">react-pdf

这种相对于原生pdfjs,简单了很多

import { useRef, useState } from 'react'
import { Document, Page, pdfjs } from 'react-pdf'
import 'react-pdf/dist/Page/AnnotationLayer.css'
import 'react-pdf/dist/Page/TextLayer.css'pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.js`interface Props {fileUrl: string
}const FilePDF = ({ fileUrl }: Props) => {const documentRef = useRef<HTMLDivElement>()const scale = useRef(1)const [pageNumber, setPageNumber] = useState<number>(1)const renderDocumentPage = (num: number, total: number) => {if (num <= total) {setPageNumber(num)requestIdleCallback(() => renderDocumentPage(num + 1, total))}}const onDocumentLoadSuccess = ({ numPages, ...rest }: { numPages: number }) => {requestIdleCallback(() => renderDocumentPage(1, numPages))}return (<divclassName={'flex h-full w-full justify-center overflow-auto rounded-lg bg-[#525659]'}><Documentref={documentRef}file={fileUrl}onLoadSuccess={onDocumentLoadSuccess}loading="努力加载中..."renderMode="canvas">{Array.from({ length: pageNumber }).map((_, i) => (<Page pageNumber={i + 1} className="mt-6" loading="努力加载中..." />))}</Document></div>)
}export default FilePDF

但是,功能太少了,如果需要添加都要自己实现一遍,也很繁琐,所以还是用了pdfjs提供的viewer来实现这个效果的

这边的效果和pdfjs-dist呈现的是一样的

pdfviewer_148">pdf-viewer

提示:使用的环境是 Vite + React

首先先根据自己的需求下载对应的build包

Getting Started

解压后,将其中的buildweb文件夹移入public中,也便后续能够直接在线上进行访问

在这里插入图片描述

这样就将 pdfjs 和 viewer 加载进来了,你可以启动项目到 /web/viewer.html 路径下访问,测试是否生效

接下来,我们对其进行封装,我通过的方式是iframe去访问 viewer 来展示pdf

interface Props {fileUrl: string
}const FilePDF = ({ fileUrl }: Props) => {return (<div className={'h-full w-full overflow-hidden rounded-lg'}><iframeclassName="border-0"title="预览文档"src={`/graphicPlatform/web/viewer.html?file=${encodeURIComponent(fileUrl)}`}width="100%"height="100%"></iframe></div>)
}export default FilePDF

注意:

在这里插入图片描述

因为文件路径是一个url链接,不能直接当作链接,需要对其特殊字符进行转义,不然 viewer 没办法识别到真正的url

接着,我们还要到viewer里去修改一下接收到的file字符串,进行还原

在这里插入图片描述

这样 viewer 才能真正接收到fileUrl

最终呈现

在这里插入图片描述

encodeURIencodeURIComponent 的区别

encodeURIComponent() 函数 与 encodeURI() 函数的区别之处,前者假定它的参数是 URI 的一部分(比如协议、主机名、路径或查询字符串)。因此 encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。

Viewer

再回到一开始的问题,我们需要禁用用户下载、打印等等功能,所以我们需要进入到 viewer 代码里进行删除对应的功能

首先在 viewer 中删除相关元素

viewer.html

在这里插入图片描述

viewer.mjs

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

删除无用文件

/web/locale

在这里插入图片描述

最终呈现
在这里插入图片描述

参考链接

  1. 前端 pdf 在线预览 - 掘金 (juejin.cn)
  2. *https://blog.csdn.net/qq_40609533/article/details/106498334*
  3. PDF.js (mozilla.github.io)

http://www.ppmy.cn/devtools/145797.html

相关文章

HuaWei、NVIDIA 数据中心 AI 算力对比

HuaWei Ascend 910B Ascend 910B 是 HuaWei 于 2023 年推出的高性能 AI 处理器芯片&#xff0c;其对标产品为 Nvidia A100/A800&#xff0c;其算力表现如下&#xff1a; 峰值算力&#xff1a;Ascend 910B 的半精度&#xff08;FP16&#xff09;算力达到 256 TFLOPS&#xff08;…

《英雄联盟》d3dcompiler_47.dll缺失是什么原因?d3dcompiler_47.dll缺失要怎么解决?

一、d3dcompiler_47.dll缺失的成因与影响 成因&#xff1a; DirectX版本不兼容&#xff1a;d3dcompiler_47.dll是DirectX的一个组件&#xff0c;负责Direct3D着色器编译。如果系统中安装的DirectX版本过低或过高&#xff0c;与游戏所需的版本不匹配&#xff0c;就可能导致d3d…

基于Spring Boot的工商局商家管理系统

一、系统背景与意义 随着市场经济的不断发展&#xff0c;商家数量日益增多&#xff0c;工商局对商家的管理需求也日益复杂。传统的管理方式存在诸多弊端&#xff0c;如信息不透明、管理效率低下等。因此&#xff0c;开发一种高效、便捷、智能化的工商局商家管理系统显得尤为重…

Linux 下的 GPT 和 MBR 分区表详解

在Linux系统中&#xff0c;分区表是描述存储设备上分区布局和属性的一种结构化数据。常见的分区表格式有MBR&#xff08;Master Boot Record&#xff09;和GPT&#xff08;GUID Partition Table&#xff09;。以下是这两种分区表的详解&#xff1a; MBR&#xff08;Master Boot…

PTA数据结构编程题7-1最大子列和问题

我参考的B站up的思路 题目 题目链接 给定K个整数组成的序列{ N 1 ​ , N 2 ​ , …, N K ​ }&#xff0c;“连续子列”被定义为{ N i ​ , N i1 ​ , …, N j ​ }&#xff0c;其中 1≤i≤j≤K。“最大子列和”则被定义为所有连续子列元素的和中最大者。例如给定序列{ -2, 1…

Java中三大构建工具的发展历程(Ant、Maven和Gradle)

&#x1f438; 背景 我们要写一个Java程序&#xff0c;一般的步骤是编译&#xff0c;测试&#xff0c;打包。 这个构建的过程&#xff0c;如果文件比较少&#xff0c;我们可以手动使用java, javac,jar命令去做这些事情。但当工程越来越大&#xff0c;文件越来越多&#xff0c…

如何在 Linux 上使用 Rsync 同步本地和远程目录

简介 Rsync&#xff0c;即 remote sync 的缩写&#xff0c;是一个文件传输和同步工具&#xff0c;可以在两个目录之间安全地复制和同步文件。其中一个必须是源目录&#xff0c;另一个是目标目录&#xff0c;目标目录也可以是远程的。它使用一种增量传输算法&#xff0c;只发送…

论文解读 | 《濒危药用真菌桑黄合成功能性多糖和黄酮的研究》

论文作者&#xff1a;马小魁&#xff0c;张红 桑黄作为传统中药已有 2000 多年的历史&#xff0c;其主要用于活血、止血、止泻、脾虚泄泻等&#xff0c;现代技术检测分析发现桑黄含有多糖、黄酮、萜类化合物和甾体类化合物等活性成分&#xff0c;这种大型药用真菌&#xff0c;…