文件預覽PDF.js使用技巧示例總結(jié)
Pdf.js有兩種使用方式
在這次的項目中用到了pdf文件的預覽功能,選擇了pdf.js去預覽pdf文件,實現(xiàn)滑動展示所有的pdf
- 通過 npm 下載
- 直接下載 pdf.js 庫,當作靜態(tài)資源使用
把pdf.js當作靜態(tài)資源使用
最開始我采取的是把pdf.js當作靜態(tài)資源使用,使用方法如下:
- 官網(wǎng)下載后解壓項目得到 pdf.js
- 放到項目文件夾 /public/static/ 下
- 直接將 web/viewer.html 后面的 file 跟上自己自己的 pdf 文件即可
contentUrl.current = content.includes("pdf") ? `/static/pdfjs-3.1.81-dist/web/viewer.html?file=${content}` : content; <iframe src={contentUrl.current}></iframe>
使用靜態(tài)資源時,如果需要更改他的默認樣式需要自己手動改源代碼
同時可能我們遇到了跨域問題,我們需要在源碼中的判斷跨域代碼注釋掉
使用靜態(tài)資源的問題是在移動端不能手勢放大縮小,需要我們自己編寫代碼然后修改源代碼強制放大縮小
這種方式不細講,網(wǎng)上很多使用方式都是通過使用靜態(tài)資源,可以自行去查看,這里只講一個大概。
npm下載,通過import使用
方法如下:
npm install pdfjs-dist
const contentRef = useRef<HTMLDivElement | null>(null); useEffect(() => { //content就是 iframe 請求的url,這里是因為我的項目里面需要判斷下他url是否涵蓋了pdf //如果涵蓋了才使用pdf.js content.includes('pdf') //重點是 loadPdf() 這個函數(shù),就是我們使用 pdf.js 的函數(shù) ? loadPdf(contentRef.current, content, loadingRef.current) : null; }, [content]); <div className="content-wrapper" ref={contentRef}> {content.includes('pdf') ? null : <iframe src={content}></iframe>} <div className="loading" ref={loadingRef} style={{ display: 'none' }}></div> </div>
import * as pdf from 'pdfjs-dist'; import pdfWorker from 'pdfjs-dist/build/pdf.worker.js?url'; pdf.GlobalWorkerOptions.workerSrc = pdfWorker; /** * @desc 使用pdf.js加載pdf * @param contentDom * @param url */ export const loadPdf = async ( contentDom: HTMLDivElement | null, url: string, loadingDom: HTMLDivElement | null ) => { //得到請求的 pdf 文件 const loadingTask = pdf.getDocument({ url: url, disableRange: true }); //loading效果,下載pdf過程中展示loading loadingTask.onProgress = () => { if (loadingDom) { loadingDom.style.display = 'block'; } }; loadingTask.promise.then((pdfDoc) => { //下載完成時,loading消失 if (loadingDom) { loadingDom.style.display = 'none'; } //得到 pdf 總頁數(shù) const totalPages = pdfDoc.numPages; for (let i = 1; i <= totalPages; i++) { pdfDoc.getPage(i).then((page) => { const canvas = document.createElement('canvas'); canvas.setAttribute('id', `the-canvas${i}`); const ctx = canvas.getContext('2d') as CanvasRenderingContext2D; const dpr = window.devicePixelRatio || 1; const scaledViewport = page.getViewport({ scale: 1 }); canvas.height = Math.floor(scaledViewport.height * dpr); canvas.width = Math.floor(scaledViewport.width * dpr); canvas.style.width = document.body.clientWidth + 'px'; canvas.style.height = document.body.clientWidth / (canvas.width / canvas.height) + 'px'; const transform = dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined; const renderContext = { canvasContext: ctx, viewport: scaledViewport, transform: transform }; page.render(renderContext); contentDom?.appendChild(canvas); }); } }); };
上面的代碼展示了我使用pdf.js的整個使用過程,主要思路是先獲取到pdf文件,然后得到總頁數(shù)后通過生成響應頁數(shù)的canvas,然后再渲染到頁面上展示pdf
這里需要注意的是scaledViewport.height得到的是你的pdf文件本身的寬高,我們需要通過這個寬高進行適配我們自己的屏幕,同時需要保證他的清晰度,所以我們需要保證canvas兩個寬高的尺寸是一致的。
canvas 本身有兩個寬高,標簽的 width 和 height 是繪畫區(qū)域?qū)嶋H寬度和高度,繪制的圖形都是在這個上面。而 style 的 width 和 height 是 canvas 在瀏覽器中被渲染的高度和寬度,如果 canvas 標簽中沒有定義 width 和 height 時,默認會給寬 300 高 150,所以就出現(xiàn)了拉伸的效果,不想用默認的寬高的話,盡量在標簽中寫上寬高的屬性。
所以,如上面的代碼一樣,寬度就是整個屏幕的寬度,但是每個pdf頁面的高度,需要保證和canvas.width/canvas.height的尺寸一致
canvas.style.height = document.body.clientWidth / (canvas.width / canvas.height) + 'px';
這樣就能展示pdf文件了
這里又存在一個問題,文件過大,而pdf.js的渲染原理是需要將整個pdf文件下載下來后,再進行展示,這就導致了白屏的時間過長,用戶體驗感不好,然后我們就想到了分片下載
我們需要在 getDocument()這個api上增加一些配置
const loadingTask = pdf.getDocument({ url: url, //disableRange: true, rangeChunkSize: 65536 * 16, disableAutoFetch: true });
分片下載還有一個很重要的點,就是需要判斷下你訪問的pdf是否支持分片下載,使用了分片下載的請求是后續(xù)會通過你的分片大小發(fā)送206請求
HTTP206狀態(tài)碼代表的意思是 請求已成功處理,但僅返回了部分內(nèi)容,即 HTTP 206 Partial Content 響應狀態(tài)。
HTTP 206 (Http Status Code 206) 狀態(tài)是HTTP協(xié)議的一種響應碼,是我們請求訪問網(wǎng)站時,服務器端返回的2xx 成功狀態(tài)系列響應碼之一。
查看是否支持分片下載
需要有這個響應頭才是支持分片下載 accept-range:bytes
但后面我們遇到一個問題:我們的pdf鏈接存在這個響應頭,但卻不支持分片下載,后面通過對比發(fā)現(xiàn)是這個響應頭 access-control-expose-headers 的問題
響應標頭 Access-Control-Expose-Headers 允許服務器指示哪些響應標頭應該對瀏覽器中運行的腳本可用,以響應跨源請求。
我們最開始這個響應頭里面的內(nèi)容沒有Access-Control-Expose-Headers,意味著就算存在 accept-range 這個響應頭,瀏覽器也不可用,所以在后面我們添加上這個響應頭的內(nèi)容后就可以使用分片下載了。
API(記錄一下,防止忘記)
這個api來源于掘金的某位大佬
屬性 | 說明 | 類型 | 默認值 |
---|---|---|---|
屬性 | 說明 | 類型 | 默認值 |
worker | 用于加載和解析 PDF 數(shù)據(jù)的工作器 | PDFWorker | - |
withCredentials | 指示是否應使用 cookie 或授權標頭等憑據(jù)發(fā)出跨站點訪問控制請求。 | Boolean | false |
verbosity | 控制日志記錄級別;應該使用 VerbosityLevel 中的常量 | Number | - |
useWorkerFetch | 在讀取 CMap 和標準字體文件時啟用在工作線程中使用 Fetch API。當為“true”時,會忽略“CMapReaderFactory”和“StandardFontDataFactory”選項。 Web 環(huán)境中的默認值為 true,Node.js 中的默認值為 false | Boolean | - |
useSystemFonts | 是否使用系統(tǒng)字體 | Boolean | false |
url | PDF的url地址 | String | URL |
stopAtErrors | 拒絕某些方法,例如getOperatorList、getTextContent 和 RenderTask,當相關的 PDF 數(shù)據(jù)無法成功解析時,而不是嘗試恢復任何可能的數(shù)據(jù) | Boolean | false |
standardFontDataUrl | 標準字體文件所在的地址。包括尾部斜杠 | String | - |
StandardFontDataFactory | 讀取標準字體文件時將使用的工廠。提供自定義工廠對于沒有 Fetch API 或 XMLHttpRequest 支持的環(huán)境很有用,例如 Node.js | Object | {DOMStandardFontDataFactory} |
rangeChunkSize | 指定每個范圍請求獲取的最大字節(jié)數(shù) | Number | DEFAULT_RANGE_CHUNK_SIZE |
range | 允許使用自定義范圍 | PDFDataRangeTransport | - |
pdfBug | 啟用用于調(diào)試 PDF.js 的特殊鉤子(請參閱web / debugger.js) | Object | false |
password | 用于解密受密碼保護的 PDF | String | - |
ownerDocument | 指定一個顯式的文檔上下文來創(chuàng)建元素并將資源(例如字體)加載到其中 | HTMLDocument | - |
maxImageSize | 總像素中允許的最大圖像大小,即寬 * 高。不會呈現(xiàn)高于此值的圖像。使用 -1 表示沒有限制,這也是默認值 | Number | - |
length | PDF 文件長度。它用于進度報告和范圍請求操作 | Number | - |
isEvalSupported | 確定我們是否可以將字符串評估為 JavaScript。主要用于提高字體渲染的性能,以及解析 PDF 函數(shù)時 | Boolean | true |
initialData | 帶有第一部分或全部 pdf 數(shù)據(jù)的類型化數(shù)組。由擴展使用,因為在切換到范圍請求之前已經(jīng)加載了一些數(shù)據(jù)。 | TypedArray | - |
httpHeaders | 基本身份驗證請求頭 | Object | - |
fontExtraProperties | 從工作線程導出解析的字體數(shù)據(jù)時,包括在渲染 PDF 文檔期間未使用的其他屬性,這對于調(diào)試目的(和向后兼容性)可能很有用,但請注意,它會導致內(nèi)存使用量增加 | Boolean | false |
enableXfa | 渲染 Xfa 表格 | Boolean | false |
docBaseUrl | 文檔的基本 URL,在嘗試恢復注釋和大綱項的有效絕對 URL 時使用,(錯誤地)僅指定了相對 URL | string | - |
disableStream | 禁用 PDF 文件數(shù)據(jù)的流式傳輸。默認情況下,PDF.js 嘗試分塊加載 PDF 文件 | Object | false |
disableRange | 禁用 PDF 文件的范圍請求加載。啟用后,如果服務器支持部分內(nèi)容請求,則 PDF 將分塊獲取 | Boolean | false |
disableFontFace | 默認情況下,字體會轉(zhuǎn)換為 OpenType 字體并通過字體加載 API 或@font-face 規(guī)則加載。如果禁用,字體將使用內(nèi)置字體渲染器渲染,該渲染器使用原始路徑命令構建字形。 | Boolean | - |
disableAutoFetch | 禁用預取 PDF 文件數(shù)據(jù)。啟用范圍請求后,即使不需要顯示當前頁面,PDF.js 也會自動繼續(xù)獲取更多數(shù)據(jù) | Object | false |
data | 二進制 PDF 數(shù)據(jù)。使用類型化數(shù)組 (Uint8Array) 來提高內(nèi)存使用率。如果 PDF 數(shù)據(jù)是 BASE64 編碼的,請先使用 atob() 將其轉(zhuǎn)換為二進制字符串。 | TypedArray、Array、String | - |
cMapUrl | 預定義 Adobe CMap 所在的 URL。包括尾部斜杠 | String | - |
CMapReaderFactory | 自定義工廠對于沒有 Fetch API 或 XMLHttpRequest 支持的環(huán)境很有用,例如 Node.js | Object | {DOMCMapReaderFactory} |
cMapPacked | 指定 Adobe CMap 是否是二進制打包 | Boolean | - |
最后,發(fā)現(xiàn)可能是這個庫的問題還是什么,目前不太清楚,他總是從13M以上才開始快速渲染,而我們當時的文件大小差不多也是13M,所以采用了分片下載后,還是存在一段比較長的白屏時間,所以最后還是選用了將pdf轉(zhuǎn)成圖片再顯示的形式。
以上就是文件預覽PDF.js使用技巧示例總結(jié)的詳細內(nèi)容,更多關于文件預覽PDF.js的資料請關注腳本之家其它相關文章!
相關文章
JavaScript判斷數(shù)據(jù)類型有幾種方法及區(qū)別介紹
這篇文章主要介紹了JavaScript判斷數(shù)據(jù)類型有幾種方法及區(qū)別介紹,本文給大家分享多種方法通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09jquery的$getjson調(diào)用并獲取遠程的JSON字符串問題
jQuery中常用getJSON來調(diào)用并獲取遠程的JSON字符串,將其轉(zhuǎn)換為JSON對象,如果成功,則執(zhí)行回調(diào)函數(shù),本文將詳細介紹,需要的朋友可以參考下2012-12-12JS中注入eval, Function等系統(tǒng)函數(shù)截獲動態(tài)代碼
這篇文章主要介紹了JS中注入eval, Function等系統(tǒng)函數(shù)截獲動態(tài)代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04實現(xiàn)圖片首尾平滑輪播(JS原生方法—節(jié)流)
下面小編就為大家?guī)硪黄獙崿F(xiàn)圖片首尾平滑輪播(JS原生方法—節(jié)流)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10