前端實現(xiàn)PDF預(yù)覽的三種方法介紹
PDF預(yù)覽的選型
對于瀏覽器自帶的PDF預(yù)覽
如果能直接使用,那自然最好不過了,但考慮多種因素,比如權(quán)限問題,禁止用戶去下載PDF、預(yù)覽樣式不統(tǒng)一(不同瀏覽器PDF預(yù)覽的實現(xiàn)不同),所有最終放棄了該方式
看了很多例子,大部分都是圍繞pdf.js這個庫展開的,所以我的選項也是圍繞它去找的
最終找到幾個不錯的
pdfjs-distreact-pdfpdf-viewer
接下來我會依次介紹一下三個庫的使用
pdfjs-dist
其實就是pdfjs庫,只是對其進行打包發(fā)布到npm了
直接根據(jù)官方文檔的案例對比進行操作就行了
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
這種實現(xiàn)比較繁瑣,所以也就有了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
但是,功能太少了,如果需要添加都要自己實現(xiàn)一遍,也很繁瑣,所以還是用了pdfjs提供的viewer來實現(xiàn)這個效果的
這邊的效果和pdfjs-dist呈現(xiàn)的是一樣的
pdf-viewer
提示:使用的環(huán)境是 Vite + React
首先先根據(jù)自己的需求下載對應(yīng)的build包
解壓后,將其中的build和web文件夾移入public中,也便后續(xù)能夠直接在線上進行訪問

這樣就將 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="預(yù)覽文檔"
src={`/graphicPlatform/web/viewer.html?file=${encodeURIComponent(fileUrl)}`}
width="100%"
height="100%"
></iframe>
</div>
)
}
export default FilePDF
注意:

因為文件路徑是一個url鏈接,不能直接當作鏈接,需要對其特殊字符進行轉(zhuǎn)義,不然 viewer 沒辦法識別到真正的url
接著,我們還要到viewer里去修改一下接收到的file字符串,進行還原

這樣 viewer 才能真正接收到fileUrl
最終呈現(xiàn)

encodeURI 和 encodeURIComponent 的區(qū)別
encodeURIComponent() 函數(shù) 與 encodeURI() 函數(shù)的區(qū)別之處,前者假定它的參數(shù)是 URI 的一部分(比如協(xié)議、主機名、路徑或查詢字符串)。因此 encodeURIComponent() 函數(shù)將轉(zhuǎn)義用于分隔 URI 各個部分的標點符號。
Viewer
再回到一開始的問題,我們需要禁用用戶下載、打印等等功能,所以我們需要進入到 viewer 代碼里進行刪除對應(yīng)的功能
首先在 viewer 中刪除相關(guān)元素
viewer.html

viewer.mjs



刪除無用文件
/web/locale

最終呈現(xiàn)

以上就是前端實現(xiàn)PDF預(yù)覽的三種方法介紹的詳細內(nèi)容,更多關(guān)于前端預(yù)覽PDF的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript實現(xiàn)一鍵復(fù)制文本功能的示例代碼
這篇文章主要為大家介紹兩種javascript實現(xiàn)文本復(fù)制(將文本寫入剪貼板)的方法,文中的示例代碼講解詳細,大家可以根據(jù)需求特點選用2023-03-03
微信小程序?qū)崿F(xiàn)tabbar凹凸圓選中動畫效果實例
小程序日益增多的情況下,UI風(fēng)格顯得越來越重要,下面這篇文章主要給大家介紹了關(guān)于微信小程序?qū)崿F(xiàn)tabbar凹凸圓選中動畫效果的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-09-09
JavaScript中的Error錯誤對象與自定義錯誤類型詳解
Error是JavaScript中最原始的錯誤對象,作為各種異常的基礎(chǔ)對象,還有多個衍生的具體的錯誤類型,這些錯誤對象類型在nodejs中也可應(yīng)用,這篇文章主要介紹了JavaScript中的Error錯誤對象與自定義錯誤類型,需要的朋友可以參考下2022-12-12
JS實現(xiàn)動態(tài)給標簽控件添加事件的方法示例
這篇文章主要介紹了JS實現(xiàn)動態(tài)給標簽控件添加事件的方法,結(jié)合實例形式分析了javascript簡單實現(xiàn)動態(tài)添加事件的相關(guān)操作技巧,需要的朋友可以參考下2017-05-05
JavaScript如何使用dhtmlXTreeObject的loadJSONObject繪制目錄樹
這篇文章主要介紹了JavaScript如何使用dhtmlXTreeObject的loadJSONObject繪制目錄樹,需要引入dhtmlXTreeObject的css和js文件,這里還需要注意js的引用順序,本文給大家介紹的非常詳細,需要的的朋友參考下吧2023-11-11

