You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js@15.2.4中pdfjs-dist@4.10.38渲染PDF文本异常排查

问题:Next.js中pdfjs-dist渲染PDF文本损坏,纯React环境正常

在Next.js@15.2.4应用中使用pdfjs-dist@4.10.38渲染PDF文件时,出现文本损坏的情况,但相同代码和依赖版本在纯React应用中可正常展示PDF。

简化代码结构:

PDFViewer.tsx(主组件)

import { GlobalWorkerOptions, getDocument } from "pdfjs-dist";
import { useEffect, useState } from "react";
import PDFPage from "./PDFPage";

GlobalWorkerOptions.workerSrc = "/pdf.worker.min.js";

export default function PDFViewer() {
  const [pdfDoc, setPdfDoc] = useState(null);

  useEffect(() => {
    getDocument("/pdf/example.pdf").promise.then(setPdfDoc);
  }, []);

  if (!pdfDoc) return null;

  return (
    <>
      {Array.from({ length: pdfDoc.numPages }, (_, i) => (
        <PDFPage key={i} pdf={pdfDoc} pageNumber={i + 1} scale={1.5} />
      ))}
    </>
  );
}

PDFPage.tsx(子组件)

import { useEffect, useRef, useState } from "react";

export default function PDFPage({ pdf, pageNumber, scale }) {
  const canvasRef = useRef(null);
  const [viewport, setViewport] = useState(null);

  useEffect(() => {
    let cancelled = false;

    pdf.getPage(pageNumber).then((page) => {
      const vp = page.getViewport({ scale });
      setViewport(vp);

      const canvas = canvasRef.current;
      const context = canvas.getContext("2d");

      const dpr = window.devicePixelRatio || 1;
      canvas.width = vp.width * dpr;
      canvas.height = vp.height * dpr;
      canvas.style.width = `${vp.width}px`;
      canvas.style.height = `${vp.height}px`;

      context.scale(dpr, dpr);

      const renderTask = page.render({ canvasContext: context, viewport: vp });
      renderTask.promise.catch((err) => {
        if (!cancelled) console.error("Render error:", err);
      });
    });

    return () => {
      cancelled = true;
    };
  }, [pdf, pageNumber, scale]);

  return (
    <div>
      <canvas ref={canvasRef} />
    </div>
  );
}

原因分析

核心问题是Next.js的服务端渲染(SSR)/静态生成(SSG)特性导致的:

  • pdfjs-dist的Worker初始化、PDF渲染依赖浏览器环境的DOM API(比如canvas.getContext)和window对象,而Next.js在服务端渲染阶段会执行组件代码,此时这些浏览器API不存在,会导致渲染逻辑异常。
  • Worker文件的路径在服务端和客户端的解析规则不同,可能导致Worker加载失败,进而影响文本渲染。

修复方案

1. 让PDF组件仅在客户端渲染

使用Next.js的dynamic导入,禁用SSR,确保组件只在浏览器端执行:
在使用PDFViewer的地方修改导入方式:

import dynamic from 'next/dynamic';

// 禁用SSR,组件仅在客户端加载
const PDFViewer = dynamic(() => import('./PDFViewer'), { ssr: false });

2. 修正Worker初始化逻辑

将Worker的初始化移到useEffect中(仅在客户端执行),同时可以改用CDN路径避免本地路径问题:
修改PDFViewer.tsx:

import { GlobalWorkerOptions, getDocument } from "pdfjs-dist";
import { useEffect, useState } from "react";
import PDFPage from "./PDFPage";

export default function PDFViewer() {
  const [pdfDoc, setPdfDoc] = useState(null);

  useEffect(() => {
    // 仅在客户端初始化Worker
    GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.10.38/pdf.worker.min.js';
    // 或者用本地public目录的文件:'/pdf.worker.min.js',确保文件已放到public目录
    getDocument("/pdf/example.pdf").promise.then(setPdfDoc);
  }, []);

  if (!pdfDoc) return null;

  return (
    <>
      {Array.from({ length: pdfDoc.numPages }, (_, i) => (
        <PDFPage key={i} pdf={pdfDoc} pageNumber={i + 1} scale={1.5} />
      ))}
    </>
  );
}

3. 增强PDFPage组件的客户端兼容性

在PDFPage组件中,确保canvas存在后再执行渲染逻辑,避免服务端渲染时的空引用错误:
修改PDFPage.tsx的useEffect部分:

useEffect(() => {
  let cancelled = false;
  const canvas = canvasRef.current;
  if (!canvas) return; // 确保canvas存在

  pdf.getPage(pageNumber).then((page) => {
    if (cancelled) return;
    const vp = page.getViewport({ scale });
    setViewport(vp);

    const context = canvas.getContext("2d");
    if (!context) return; // 确保获取到canvas上下文

    const dpr = window.devicePixelRatio || 1;
    canvas.width = vp.width * dpr;
    canvas.height = vp.height * dpr;
    canvas.style.width = `${vp.width}px`;
    canvas.style.height = `${vp.height}px`;

    context.scale(dpr, dpr);

    const renderTask = page.render({ canvasContext: context, viewport: vp });
    renderTask.promise.catch((err) => {
      if (!cancelled) console.error("Render error:", err);
    });
  });

  return () => {
    cancelled = true;
  };
}, [pdf, pageNumber, scale]);

4. 验证资源路径

  • 确保pdf.worker.min.js已放到Next.js的public目录下(如果用本地文件),路径对应/pdf.worker.min.js。
  • 确保PDF文件/pdf/example.pdf存在于public/pdf/目录下。

内容的提问来源于stack exchange,提问作者Itay Tur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:27:05