Advertisement

React 实现 PDF 文件在线预览 - 手把手教你写 React PDF 预览功能

阅读量:
React 实现 PDF 文件在线预览 - 手把手教你写 React PDF 预览功能

完整教程:《React 实现 PDF 文件在线预览 - 手把手教你写 React PDF 预览功能

React 实现 PDF 文件在线预览

  • 快速搭建应用
  • 开发预览组件用于展示 PDF 的第一页内容
  • 开发预览组件用于完整展示 PDF 并实现翻页功能
  • 选择 PDF 文本块
  • 在线查看 PDF 源代码块
  • 构建基于 React 的 PDFjs 框架并实现云服务功能

对于开发中的 React 项目而言,在多种应用场景下都不可避免地需要 PDF 文件的在线查看功能。这些应用场景包括合同 ERP、销售 CRM 和内部文档 CMS 管理系统等。本篇文章将详细指导读者如何将 PDF 预览组件集成到 React 应用中,并实现一系列常见的操作流程。

完成本教程的学习后,你会搭建出以下 PDF 在线查看效果的 React PDF 预览组件 https://kalacloud.com/blog/how-build-react-pdf-viewer-pdfjs/

React PDFjs 搭建效果

当你需要构建后端管理系统而不必涉及前端开发时,请考虑使用卡拉云这款新兴的低代码平台

本次教程中使用的技术栈

  • Vite
  • React
  • Typescript
  • pdf.js

快速搭建项目

复制代码
    > yarn create vite pdf-preview --template react-ts

现在我们安装下 pdf.js

官网并未提供关于 npm 下载方式的具体指导。
因此,在这种情况下,默认安装/umd版本是常见的做法。
实际上,在使用一个库时,默认会查看文档和官方示例并不是唯一的最佳实践。
从源代码中的 examples/webpack/main.js 文件中可以看到 pdfjs-dist 这个 npm 包。
我们可以进行下载。

然后按照自己的习惯组织下文件目录

复制代码
    .
    ├── components
    │   └── PDFRender
    │       └── index.tsx
    ├── main.tsx
    ├── App.tsx
    └── vite-env.d.ts

了解五款开源React移动端UI组件库的测评与推荐 https://kalacloud.com/blog/best-react-mobile-ui-component-libraries/

渲染第一页 - React 开发预览组件

为了简化流程和提高效率,在开发过程中我打算创建一个新的PDFRender组件,并负责将PDF文件的第一页内容进行展示。

复制代码
    import * as pdf from 'pdfjs-dist'
    import pdfWorker from 'pdfjs-dist/build/pdf.worker.js?url'
    import React, { useLayoutEffect, useRef } from "react";
    
    pdf.GlobalWorkerOptions.workerSrc = pdfWorker;
    
    export const PDFRender: React.FC<{ src: string }> = (props) => {
      const canvasRef = useRef<HTMLCanvasElement | null>(null)
      useLayoutEffect(() => {
    pdf
    .getDocument(props.src)
    .promise
    .then(pdfDocument => {
      return pdfDocument.getPage(1);
    })
    .then((pdfPage) => {
      const viewport = pdfPage.getViewport({ scale: 1.0 });
      const canvas = canvasRef.current;
      if (!canvas) {
        return Promise.reject()
      }
      canvas.width = viewport.width
      canvas.height = viewport.height;
      const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
      const renderTask = pdfPage.render({
        canvasContext: ctx,
        viewport,
      });
      return renderTask.promise;
    })
    .catch(err => {
      console.log(err)
    })
      }, [])
      return (
    <canvas ref={canvasRef}/>
      )
    }
    
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/NUoKXDwVIGx7M5z90EutQAiq2YWJ.png)

细心的同学可能发现了这两行代码

复制代码
    import pdfWorker from 'pdfjs-dist/build/pdf.worker.js?url'
    pdf.GlobalWorkerOptions.workerSrc = pdfWorker;

由于PDF的交互可能会造成JavaScript阻塞问题,因此PDF.js采用了Web Worker技术来提升性能

最后我们使用下这个组件,看下效果

复制代码
    import { PDFRender } from "./components/PDFRender";
    
    const pdfFilePath = '/kalacloud-demo.pdf'
    
    export const App = () => {
      return (
    <PDFRender src={pdfFilePath} />
      )
    }

效果如下

react嵌入pdfjs

代码简单讲解下

  1. 调用 getDocument 函数获取 pdf 文档内容
  2. 调用 getPage 方法获取当前页面内容
  3. 利用 canvas 绘制当前页面

深入学习推荐:《优质主流 React UI 组件库评测指南

渲染整个 PDF 并翻页 - React 开发预览组件

采用该方案实现页面渲染不复杂。参考之前的方法确定页数后,通过循环结构进行页面渲染。

复制代码
    import * as pdf from 'pdfjs-dist'
    import pdfWorker from 'pdfjs-dist/build/pdf.worker.js?url'
    import { useEffect, useRef, useState } from "react";
    
    pdf.GlobalWorkerOptions.workerSrc = pdfWorker;
    
    export const usePDFData = (options: { src: string, scale?: number }) => {
      const previewUrls = useRef<string[]>([])
      const urls = useRef<string[]>([])
      const [loading, setLoading] = useState(true)
    
      useEffect(() => {
    urls.current = []
    setLoading(true)
    ;(async () => {
      // 这里千万别解构,会导致 this 指向错误
      const pdfDocument = await pdf.getDocument(options.src).promise
      const task = new Array(pdfDocument.numPages).fill(null)
      await Promise.all(task.map(async (_, i) => {
        const page = await pdfDocument.getPage(i + 1)
        const viewport = page.getViewport({ scale: options.scale || 2 })
        const canvas = document.createElement('canvas')
    
        canvas.width = viewport.width
        canvas.height = viewport.height
        const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
        const renderTask = page.render({
          canvasContext: ctx,
          viewport,
        });
        await renderTask.promise;
        // 分别获取不同尺寸的图片,一个用来预览一个用来展示
        urls.current[i] = canvas.toDataURL('image/jpeg', 1)
        previewUrls.current[i] = canvas.toDataURL('image/jpeg', 0.5)
      }))
      setLoading(false)
    })()
      }, [options.src])
    
      return {
    loading,
    urls: urls.current,
    previewUrls: previewUrls.current,
      }
    }
    
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/UBbrIncV8QHoZPDg3theE6RS9xlG.png)

接下来我们实现滚动翻页功能

  1. 点击对应页滚动到指定的位置
  2. 滚动到对应位置,高亮当前页

先看下最终的效果

React PDFjs 搭建效果

为了实现点击滚动到对应的位置这一功能, 这一过程非常简便, 可以通过调用scrollIntoView方法快速定位至所需位置

复制代码
      const goPage = (i: number) => {
    setCurrentPage(i)
    document.querySelectorAll('.page')[i]!.scrollIntoView({ behavior: 'smooth' })
      }

再来实现下滚动位置自动高亮页数

主要通过 IntersectionObserver api 实现功能,并对其结果进行判断以确定当前页面。具体来说,在检测到某页面的显示状态时(即该页面在视口中可被看到),若其可见性达到或超过50%,则认为当前页面有一半的内容处于显示范围内。

复制代码
      const io = useRef(new IntersectionObserver((entries) => {
    entries.forEach(item => {
      item.intersectionRatio >= 0.5 && setCurrentPage(Number(item.target.getAttribute('index')))
    })
      }, {
    threshold: [0.5]
      }))

作为专业的技术平台,KalaCloud公司推出了其旗舰级产品——《最佳React后台管理系统架构》,该软件旨在为开发者打造高效、安全的行政管理解决方案,并提供了丰富的功能模块以满足企业需求

PDF 文本选择

在某些特定情况下(即特殊场景),可能会有必要支持用户复制PDF上的文字内容。显而易见的是,在图片中无法直接选择文字进行操作。然而,强大的 pdf.js 工具能够提供相同的文字位置来绘制内容,并且我们计划实现这一功能。下一步我们将实现这一目标并完成相关功能开发工作

复制代码
    import * as pdf from 'pdfjs-dist'
    import pdfWorker from 'pdfjs-dist/build/pdf.worker.js?url'
    + import { TextLayerBuilder } from 'pdfjs-dist/web/pdf_viewer';
    + import 'pdfjs-dist/web/pdf_viewer.css';
    import { useEffect, useRef, useState } from "react";
    
    pdf.GlobalWorkerOptions.workerSrc = pdfWorker;
    
    export const usePDFData = (options: { src: string, scale?: number }) => {
      const previewUrls = useRef<string[]>([])
      const pages = useRef<{ canvas: HTMLCanvasElement, text: HTMLDivElement }[]>([])
      const [loading, setLoading] = useState(true)
    
      useEffect(() => {
    pages.current = []
    setLoading(true)
    ;(async () => {
      const pdfDocument = await pdf.getDocument(options.src).promise
      const task = new Array(pdfDocument.numPages).fill(null)
      await Promise.all(task.map(async (_, i) => {
        const page = await pdfDocument.getPage(i + 1)
        const viewport = page.getViewport({ scale: options.scale || 2 })
        const canvas = document.createElement('canvas')
    
        canvas.width = viewport.width
        canvas.height = viewport.height
        const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
        const renderTask = page.render({
          canvasContext: ctx,
          viewport,
        });
        await renderTask.promise;
        previewUrls.current[i] = canvas.toDataURL('image/jpeg', 0.5)
     +       const textContent = await page.getTextContent()
     +       const textLayerDiv = document.createElement('div');
     +       textLayerDiv.setAttribute('class', 'textLayer');
     +       const textLayer = new TextLayerBuilder({
     +         textLayerDiv,
     +         pageIndex: i + 1,
     +         viewport,
     +         eventBus: undefined
     +      });
     +
     +       textLayer.setTextContent(textContent);
     +       textLayer.render();
    
        pages.current[i] = {
          canvas,
          text: textLayerDiv
        }
      }))
      setLoading(false)
    })()
      }, [options.src])
    
      return {
    loading,
    pages: pages.current,
    previewUrls: previewUrls.current,
      }
    }
    
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/RH7CAZjNxlgYmJX28UKz6vnLpda3.png)

进一步了解React Echarts 使用教程 - 如何在 React 加入图表

React PDF 在线预览源代码

请访问 github 以查看本次教程的源代码

假如你只需要预览 PDF 并且不关心浏览器兼容,那么使用 embed 只需要一行代码就能实现

复制代码
    <!DOCTYPE html>
    <html lang="en">
      <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      body {
        height: 100%;
        width: 100%;
        overflow: hidden;
        background-color: rgb(82, 86, 89);
      }
      embed {
        width: 100vw;
        height: 100vh;
      }
    </style>
      </head>
      <body>
      <embed src="/kalacloud-demo.pdf" type="application/pdf">
      </body>
    </html>
    
    
    
![](https://ad.itadn.com/c/weblog/blog-img/images/2025-08-18/y3pbxV1NG0ZfW4SYTacnkQMjerXv.png)

拓展阅读:《优质开源 React Table 表格组件评测指南

React PDFjs 搭建总结及卡拉云

本文阐述了在React框架中实现PDF预览功能的方法。若不关注前端开发,则可借助现成工具来简化工作流程,减少开发负担。具体而言,我们强烈推荐使用卡拉云,该平台集成了丰富多样的组件,用户无需掌握任何前端知识,只需通过拖拽即可快速搭建完善的解决方案。

卡拉云能够帮助您迅速构建企业内部工具体系。这一图展示了使用卡拉云搭建的内部广告投放监测系统,请注意:无需前端开发知识即可操作;完成整个搭建过程仅需十分钟的时间。另外一种选择是快速构建专属于您的后端管理系统。(https://kalacloud.com/)

卡拉云企业内部工具

卡拉云(https://kalacloud.com/)是一款新兴的低代码开发平台,在与Vue、React等主流前端框架相比时有明显优势。其显著优势在于无需预先搭建开发环境即可立即开始使用;对于开发者而言无需处理任何前端问题;通过简单的拖拽操作即可快速生成所需组件;此外还支持一键接入常见数据库及API;按照引导步骤只需简单几步即可实现前后端无缝连接;将几周的开发时间缩短至1小时;现可立即免费体验(https://my.kalacloud.com/signup)。

扩展阅读:

  • 完整解析React Router 6(React路由):最详尽学习指南
  • 全栈开发实践:基于React与Node.js构建上传图片/预览功能的管理后台系统
  • 深入解析React Draggable组件:最全面拖拽功能实现技巧
  • 推荐盘点:当前最值得选用的8款 React Datepicker时间日期选择器组件测评与对比分析
  • Mastering React Form表单验证技术:从入门到精通的详细指南

全部评论 (0)

还没有任何评论哟~