前端实现PDF预览的三种方法介绍
作者:JinSoooo
PDF预览的选型
对于浏览器自带的PDF预览
如果能直接使用,那自然最好不过了,但考虑多种因素,比如权限问题,禁止用户去下载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
进行了一层封装
效果展示
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包
解压后,将其中的build
和web
文件夹移入public中,也便后续能够直接在线上进行访问
这样就将 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
注意:
因为文件路径是一个url链接,不能直接当作链接,需要对其特殊字符进行转义,不然 viewer 没办法识别到真正的url
接着,我们还要到viewer里去修改一下接收到的file字符串,进行还原
这样 viewer 才能真正接收到fileUrl
最终呈现
encodeURI 和 encodeURIComponent 的区别
encodeURIComponent() 函数 与 encodeURI() 函数的区别之处,前者假定它的参数是 URI 的一部分(比如协议、主机名、路径或查询字符串)。因此 encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。
Viewer
再回到一开始的问题,我们需要禁用用户下载、打印等等功能,所以我们需要进入到 viewer 代码里进行删除对应的功能
首先在 viewer 中删除相关元素
viewer.html
viewer.mjs
删除无用文件
/web/locale
最终呈现
以上就是前端实现PDF预览的三种方法介绍的详细内容,更多关于前端预览PDF的资料请关注脚本之家其它相关文章!