前端实现PDF预览的三种方法介绍

作者:百宝袋

时间:2026-10-31

浏览:277

本文介绍前端实现PDF预览的三种方案:浏览器原生预览、基于pdfjs-dist的封装以及react-pdf和pdf-viewer库。文章对比了各方案的优缺点,包括权限控制、功能丰富度及实现复杂度,并提供了基于Vite和React环境的具体配置与封装示例,帮助用户根据实际需求选择合适的预览方式。

文章目录

PDF预览方案选型

针对浏览器内置PDF预览功能

若可直接使用则最佳,但受限于权限控制(如禁止下载)、预览样式不一致(各浏览器实现差异)等多重因素,最终未采用此方案

经调研发现多数示例均基于pdf.js库,因此我的选型也围绕该库展开

最终筛选出几个表现良好的方案

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

下文将依次介绍这三个库的具体用法

pdfjs-dist

该库实为pdfjs库的npm打包发布版本

参照官方文档案例进行对比操作即可

示例代码

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进行了封装

效果演示

PDF预览方案选型

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与web文件夹移至public目录,以便后续在线访问

PDF预览方案选型

至此已将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

注意事项:

PDF预览方案选型

由于文件路径为URL链接,不能直接作为链接使用,需对特殊字符进行转义,否则viewer无法识别真实URL

接着,需在viewer中修改接收到的file字符串,进行还原处理

PDF预览方案选型

如此viewer方能正确接收fileUrl

最终效果

PDF预览方案选型

encodeURI与encodeURIComponent的区别

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

Viewer配置

回到初始问题,需禁用用户下载、打印等功能,故需进入viewer代码中移除对应功能模块

首先在viewer中移除相关元素

viewer.html文件

PDF预览方案选型

viewer.mjs文件

PDF预览方案选型

PDF预览方案选型

PDF预览方案选型

删除无用文件

/web/locale目录

PDF预览方案选型

最终效果

PDF预览方案选型

以上为前端实现PDF预览三种方法的详细介绍,更多关于前端预览PDF的资料请关注本站其他相关文章!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章