前端實(shí)現(xiàn)PDF預(yù)覽的三種方法介紹
PDF預(yù)覽的選型
對(duì)于瀏覽器自帶的PDF預(yù)覽
如果能直接使用,那自然最好不過了,但考慮多種因素,比如權(quán)限問題,禁止用戶去下載PDF、預(yù)覽樣式不統(tǒng)一(不同瀏覽器PDF預(yù)覽的實(shí)現(xiàn)不同),所有最終放棄了該方式
看了很多例子,大部分都是圍繞pdf.js
這個(gè)庫(kù)展開的,所以我的選項(xiàng)也是圍繞它去找的
最終找到幾個(gè)不錯(cuò)的
pdfjs-dist
react-pdf
pdf-viewer
接下來我會(huì)依次介紹一下三個(gè)庫(kù)的使用
pdfjs-dist
其實(shí)就是pdfjs庫(kù),只是對(duì)其進(jìn)行打包發(fā)布到npm了
直接根據(jù)官方文檔的案例對(duì)比進(jìn)行操作就行了
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) // 依次渲染所有頁(yè)面 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
這種實(shí)現(xiàn)比較繁瑣,所以也就有了react-pdf
,對(duì)pdfjs-dist
進(jìn)行了一層封裝
效果展示
react-pdf
這種相對(duì)于原生pdfjs,簡(jiǎn)單了很多
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
但是,功能太少了,如果需要添加都要自己實(shí)現(xiàn)一遍,也很繁瑣,所以還是用了pdfjs提供的viewer來實(shí)現(xiàn)這個(gè)效果的
這邊的效果和pdfjs-dist呈現(xiàn)的是一樣的
pdf-viewer
提示:使用的環(huán)境是 Vite + React
首先先根據(jù)自己的需求下載對(duì)應(yīng)的build包
解壓后,將其中的build
和web
文件夾移入public中,也便后續(xù)能夠直接在線上進(jìn)行訪問
這樣就將 pdfjs 和 viewer 加載進(jìn)來了,你可以啟動(dòng)項(xiàng)目到 /web/viewer.html
路徑下訪問,測(cè)試是否生效
接下來,我們對(duì)其進(jìn)行封裝,我通過的方式是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="預(yù)覽文檔" src={`/graphicPlatform/web/viewer.html?file=${encodeURIComponent(fileUrl)}`} width="100%" height="100%" ></iframe> </div> ) } export default FilePDF
注意:
因?yàn)槲募窂绞且粋€(gè)url鏈接,不能直接當(dāng)作鏈接,需要對(duì)其特殊字符進(jìn)行轉(zhuǎn)義,不然 viewer 沒辦法識(shí)別到真正的url
接著,我們還要到viewer里去修改一下接收到的file字符串,進(jìn)行還原
這樣 viewer 才能真正接收到fileUrl
最終呈現(xiàn)
encodeURI 和 encodeURIComponent 的區(qū)別
encodeURIComponent() 函數(shù) 與 encodeURI() 函數(shù)的區(qū)別之處,前者假定它的參數(shù)是 URI 的一部分(比如協(xié)議、主機(jī)名、路徑或查詢字符串)。因此 encodeURIComponent() 函數(shù)將轉(zhuǎn)義用于分隔 URI 各個(gè)部分的標(biāo)點(diǎn)符號(hào)。
Viewer
再回到一開始的問題,我們需要禁用用戶下載、打印等等功能,所以我們需要進(jìn)入到 viewer 代碼里進(jìn)行刪除對(duì)應(yīng)的功能
首先在 viewer 中刪除相關(guān)元素
viewer.html
viewer.mjs
刪除無用文件
/web/locale
最終呈現(xiàn)
以上就是前端實(shí)現(xiàn)PDF預(yù)覽的三種方法介紹的詳細(xì)內(nèi)容,更多關(guān)于前端預(yù)覽PDF的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js函數(shù)定時(shí)器實(shí)現(xiàn)定時(shí)讀取系統(tǒng)實(shí)時(shí)連接數(shù)
這篇文章主要介紹了使用js函數(shù)定時(shí)器實(shí)現(xiàn)定時(shí)讀取系統(tǒng)實(shí)時(shí)連接數(shù),需要的朋友可以參考下2014-04-04javascript實(shí)現(xiàn)移動(dòng)的模態(tài)框效果
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)移動(dòng)的模態(tài)框效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09js小數(shù)運(yùn)算出現(xiàn)多位小數(shù)如何解決
關(guān)于JavaScript小數(shù)進(jìn)行加、減、乘、除出現(xiàn)多位小數(shù)問題,自己研究了一下,而且作了相關(guān)的測(cè)試,收獲不少知識(shí)點(diǎn),需要的朋友可以參考下2015-10-10JS基于VML技術(shù)實(shí)現(xiàn)的五角星禮花效果代碼
這篇文章主要介紹了JS基于VML技術(shù)實(shí)現(xiàn)的五角星禮花效果代碼,涉及JavaScript數(shù)學(xué)運(yùn)算與頁(yè)面元素動(dòng)態(tài)操作的相關(guān)技巧,需要的朋友可以參考下2015-10-10