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

打印 上一主题 下一主题

主题 894|帖子 894|积分 2682

记录
  PDF预览的选型

对于欣赏器自带的PDF预览

如果能直接使用,那自然最好不外了,但思量多种因素,比如权限问题,克制用户去下载PDF、预览样式不统一(不同欣赏器PDF预览的实现不同),所有最终放弃了该方式
看了很多例子,大部分都是围绕pdf.js这个库展开的,所以我的选项也是围绕它去找的
最终找到几个不错的


  • pdfjs-dist
  • react-pdf
  • pdf-viewer
接下来我会依次介绍一下三个库的使用
pdfjs-dist

其实就是pdfjs库,只是对其进行打包发布到npm了
直接根据官方文档的案例对比进行操纵就行了
Examples
  1. import { useEffect, useRef } from 'react'
  2. import * as PDFJS from 'pdfjs-dist'
  3. PDFJS.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.js`
  4. interface Props {
  5.   fileUrl: string
  6. }
  7. const FilePDF = ({ fileUrl }: Props) => {
  8.   const pdfContainer = useRef<HTMLCanvasElement>(null)
  9.   const pdfCtx = useRef<CanvasRenderingContext2D | null>(null)
  10.   const pdfDoc = useRef<any>()
  11.   const pdfNumPages = useRef(0)
  12.         // 依次渲染所有页面
  13.   const renderPage = num => {
  14.     pdfDoc.current!.getPage(num).then(page => {
  15.       const viewport = page.getViewport({ scale: 1 })
  16.       pdfContainer.current!.width = viewport.width
  17.       pdfContainer.current!.height = viewport.height
  18.       page
  19.         .render({
  20.           viewport,
  21.           canvasContext: pdfCtx.current!
  22.         })
  23.         .promise.then(() => {
  24.           if (num < pdfNumPages.current) {
  25.             renderPage(num + 1)
  26.           }
  27.         })
  28.     })
  29.   }
  30.   useEffect(() => {
  31.     pdfCtx.current = pdfContainer.current!.getContext('2d')
  32.     PDFJS.getDocument(fileUrl).promise.then(pdfDoc_ => {
  33.       pdfDoc.current = pdfDoc_
  34.       pdfNumPages.current = pdfDoc_.numPages
  35.       renderPage(1)
  36.     })
  37.   }, [])
  38.   return (
  39.     <div className={'flex h-full w-full items-center justify-center rounded-lg'}>
  40.       <canvas ref={pdfContainer}></canvas>
  41.     </div>
  42.   )
  43. }
  44. export default FilePDF
复制代码
这种实现比较繁琐,所以也就有了react-pdf,对pdfjs-dist进行了一层封装
效果展示

react-pdf

这种相对于原生pdfjs,简单了很多
  1. import { useRef, useState } from 'react'
  2. import { Document, Page, pdfjs } from 'react-pdf'
  3. import 'react-pdf/dist/Page/AnnotationLayer.css'
  4. import 'react-pdf/dist/Page/TextLayer.css'
  5. pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/legacy/build/pdf.worker.min.js`
  6. interface Props {
  7.   fileUrl: string
  8. }
  9. const FilePDF = ({ fileUrl }: Props) => {
  10.   const documentRef = useRef<HTMLDivElement>()
  11.   const scale = useRef(1)
  12.   const [pageNumber, setPageNumber] = useState<number>(1)
  13.   const renderDocumentPage = (num: number, total: number) => {
  14.     if (num <= total) {
  15.       setPageNumber(num)
  16.       requestIdleCallback(() => renderDocumentPage(num + 1, total))
  17.     }
  18.   }
  19.   const onDocumentLoadSuccess = ({ numPages, ...rest }: { numPages: number }) => {
  20.     requestIdleCallback(() => renderDocumentPage(1, numPages))
  21.   }
  22.   return (
  23.     <div
  24.       className={
  25.         'flex h-full w-full justify-center overflow-auto rounded-lg bg-[#525659]'
  26.       }
  27.     >
  28.       <Document
  29.         ref={documentRef}
  30.         file={fileUrl}
  31.         onLoadSuccess={onDocumentLoadSuccess}
  32.         loading="努力加载中..."
  33.         renderMode="canvas"
  34.       >
  35.         {Array.from({ length: pageNumber }).map((_, i) => (
  36.           <Page pageNumber={i + 1} className="mt-6" loading="努力加载中..." />
  37.         ))}
  38.       </Document>
  39.     </div>
  40.   )
  41. }
  42. export default FilePDF
复制代码
但是,功能太少了,如果需要添加都要本身实现一遍,也很繁琐,所以还是用了pdfjs提供的viewer来实现这个效果的
   这边的效果和pdfjs-dist出现的是一样的
  pdf-viewer

   提示:使用的环境是 Vite + React
  首先先根据本身的需求下载对应的build包
Getting Started
解压后,将此中的build和web文件夹移入public中,也便后续能够直接在线上进行访问

这样就将 pdfjs 和 viewer 加载进来了,你可以启动项目到 /web/viewer.html 路径下访问,测试是否生效
接下来,我们对其进行封装,我通过的方式是iframe去访问 viewer 来展示pdf的
  1. interface Props {
  2.   fileUrl: string
  3. }
  4. const FilePDF = ({ fileUrl }: Props) => {
  5.   return (
  6.     <div className={'h-full w-full overflow-hidden rounded-lg'}>
  7.       <iframe
  8.         className="border-0"
  9.         title="预览文档"
  10.         src={`/graphicPlatform/web/viewer.html?file=${encodeURIComponent(fileUrl)}`}
  11.         width="100%"
  12.         height="100%"
  13.       ></iframe>
  14.     </div>
  15.   )
  16. }
  17. export default FilePDF
复制代码
注意:

因为文件路径是一个url链接,不能直接看成链接,需要对其特殊字符进行转义,不然 viewer 没办法识别到真正的url
接着,我们还要到viewer里去修改一下接收到的file字符串,进行还原

这样 viewer 才气真正接收到fileUrl
最终出现

encodeURI 和 encodeURIComponent 的区别

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

再回到一开始的问题,我们需要禁用用户下载、打印等等功能,所以我们需要进入到 viewer 代码里进行删除对应的功能
首先在 viewer 中删除相关元素
   viewer.html
  

   viewer.mjs
  



删除无用文件
   /web/locale
  

最终出现

参考链接


  • 前端 pdf 在线预览 - 掘金 (juejin.cn)
  • *https://blog.csdn.net/qq_40609533/article/details/106498334*
  • PDF.js (mozilla.github.io)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

圆咕噜咕噜

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表