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

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

记录

PDF预览的选型

对于浏览器自带的PDF预览

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

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

最终找到几个不错的

  • pdfjs-dist
  • react-pdf
  • pdf-viewer

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

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.width
      pdfContainer.current!.height = viewport.height

      page
        .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_.numPages
      renderPage(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进行了一层封装

效果展示

在这里插入图片描述

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 (
    <div
      className={
        'flex h-full w-full justify-center overflow-auto rounded-lg bg-[#525659]'
      }
    >
      <Document
        ref={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呈现的是一样的

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'}>
      <iframe
        className="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.kler.cn/a/460499.html

相关文章:

  • 大模型系列——旋转位置编码和长度外推
  • 出现 Error during query execution: StatementCallback; bad SQL grammar 解决方法
  • 《从入门到精通:蓝桥杯编程大赛知识点全攻略》(一)-递归实现指数型枚举、递归实现排列型枚举
  • html 音频和视频组件
  • Unity学习笔记(五)什么是状态机
  • HAL 库------中断相关函数
  • 嵌入式科普(25)Home Assistant米家集成意味着IOT的核心是智能设备
  • 形象地理解UE4中的数据结构 TLinkedListBase
  • [极客大挑战 2019]LoveSQL 1解题思路
  • Java多线程中,synchronized和ReentrantLock的区别是什么?及使用场景有哪些?
  • html+css网页设计 美食 美食美客模版2个页面
  • python 选择排序(Selection Sort)
  • 纯前端实现将pdf转为图片(插件pdfjs)
  • 优化大肠杆菌菌株和发酵工艺以提高L-赖氨酸生产-文献精读94
  • 如何修复 WordPress 中的“Error establishing a database connection”问题
  • DeepSeek-V3-Base 模型技术解析
  • 智能工厂的设计软件 应用场景的一个例子:为AI聊天工具添加一个知识系统 之10 方案再探:特定于领域的模板 之1 随想交流
  • 口语笔记——感官+花费
  • MySQL数据库的锁
  • ubuntu 使用samba与windows共享文件[注意权限配置]
  • 留学生该如何进行文学分析类的essay写作
  • 分析电控发动机常见故障原因
  • vue使用el-select下拉框自定义复选框
  • IDEA修改编译版本
  • [2025] 如何在 Windows 计算机上轻松越狱 IOS 设备
  • 什么是 GPT?Transformer 工作原理的动画展示