如何在React中集成 PDF.js?构建支持打印下载的PDF阅读器详解

React集成PDF.js构建PDF阅读器详解

本文深入解析基于 React 和 PDF.js 构建 PDF 查看器的实现方案,该组件支持 PDF 渲染、图片打印和下载功能,并包含完整的加载状态与错误处理机制。

完整代码在最后

一个PDF 文件:

https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf

效果展示:

在这里插入图片描述

安装 PDF.js

要使用 pdf.js,你可以通过 npm 安装它。以下是安装和配置 pdf.js 的详细步骤:

1. 安装 pdfjs-dist

pdf.js 的官方 npm 包名为 pdfjs-dist。可以通过以下命令安装:

npm install pdfjs-dist
  • pdfjs-dist 是 PDF.js 的分发版本,包含了核心功能和 Worker 文件。
  • 安装完成后,你可以在项目中直接引入并使用。

2. 设置 Worker 文件路径

PDF.js 需要一个 Worker 文件来处理 PDF 渲染任务。默认情况下,Worker 文件路径需要手动设置。

方法 1:使用 CDN

如果你不想将 Worker 文件打包到项目中,可以直接使用 CDN 提供的 Worker 文件:

import {
   
    GlobalWorkerOptions } from 'pdfjs-dist';

// 设置 Worker 文件路径
GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';
方法 2:本地 Worker 文件

如果你希望将 Worker 文件打包到项目中,可以这样做:

  1. 在代码中引入 Worker 文件:

    import {
         
          GlobalWorkerOptions, getDocument } from 'pdfjs-dist';
    import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
    
    // 设置 Worker 文件路径
    GlobalWorkerOptions.workerSrc = pdfjsWorker;
    
  2. 这种方式适合离线环境或需要完全控制依赖的情况。

技术架构解析

1. 核心依赖
  • PDF.js:Mozilla 开源的 PDF 解析库,支持 Web 端 PDF 渲染
  • React Hooks:使用 useState 管理状态,useEffect 处理副作用,useRef 操作 DOM
  • Ant Design:采用 Button 组件构建操作界面

pdf.js 官网:https://mozilla.github.io/pdf.js/

2. 初始化配置

通过配置 Worker 文件实现 PDF 解析的 Web Worker 并行处理,避免阻塞主线程。

GlobalWorkerOptions.workerSrc = '//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js';

核心功能实现

1. PDF 渲染流程

这段代码的核心功能是加载一个 PDF 文件,并将其第一页渲染到 <canvas> 元素中。

// 加载 PDF 并渲染到 canvas
  useEffect(() => {
   
   
    const abortController = new AbortController();

    if (!pdfUrl) return;

    setIsLoading(true);
    setError(null);

    getDocument({
   
   
      url: pdfUrl,
      cMapUrl: '/cmaps/', // 引入字体映射文件
      cMapPacked: true, // 启用字体映射文件
    }).promise
      .then((pdf) => {
   
   
        if (abortController.signal.aborted) return;

        console.log('PDF loaded');

        // 获取第一页
        pdf.getPage(1).then((page) => {
   
   
          if (abortController.signal.aborted) {
   
   
            page.cleanup();
            return;
          }

          console.log('Page 1 loaded');
          const scale = 1.5;
          const viewport = page.getViewport({
   
    scale });

          if (!canvasRef.current) {
   
   
            throw new Error('Canvas element not found');
          }
          const canvas = canvasRef.current;
          const context = canvas.getContext('2d');

          // 清除之前的画布内容
          context?.clearRect(0, 0, canvas.width, canvas.height);

          canvas.height = viewport.height;
          canvas.width = viewport.width;
          console.log('Canvas尺寸设置:', canvas.width, 'x', canvas.height);

          // 渲染 PDF 页面到 canvas
          const renderContext: any = {
   
   
            canvasContext: context,
            viewport: viewport,
          };

          const renderTask = page.render(renderContext);

          renderTask.promise
            .then(() => {
   
   
              console.log('页面渲染完成');
              setIsLoading(false);
            })
            .catch((renderError) => {
   
   
              if (!abortController.signal.aborted) {
   
   
                setError('页面渲染失败: ' + renderError.message);
              }
            });
        });
      })
      .catch((error) => {
   
   
        if (!abortController.signal.aborted) {
   
   
          setError('PDF加载失败: ' + error.message);
          console.error('Error loading PDF:', error);
        }
      })
      .finally(() => {
   
   
        if (!abortController.signal.aborted) {
   
   
          setIsLoading(false);
        }
      });

    return () => {
   
   
      abortController.abort();
      const canvas = canvasRef.current;
      if (canvas) {
   
   
        const context = canvas.getContext('2d');
        context?.clearRect(0, 0, canvas.width, canvas.height);
      }
    };
  }, [pdfUrl]);
  • 初始化加载状态:设置加载提示和清理错误信息。
  • 加载 PDF 文件:使用 getDocument 加载 PDF,并获取第一页。
  • 计算视口与画布尺寸:根据缩放比例调整 <canvas> 的大小。
  • 渲染 PDF 页面:将 PDF 页面绘制到 <canvas> 上。
  • 错误处理与清理:捕获加载和渲染中的错误,并在组件卸载时清理资源。

以下是核心实现原理与技术要点的详细解读:

1. useEffect 的作用
useEffect(() 
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值