Pdf.js body.getReader 报错问题
標題也可以改成:pdf.js 使用 fetch, axios 來渲染獲取渲染內容
一、問題背景描述
在日常工作中,使用 pdf.js 做 文件的預覽,是一個很常見的需求!
而且這個組件非常強大,背后的維護組織是:mozilla,火狐瀏覽器的組織,所以非常的強大!
這里放一段 pdf.js 官方 github 倉庫的示例代碼:
const pdfPath = "../learning/helloworld.pdf";// Setting worker path to worker bundle. pdfjsLib.GlobalWorkerOptions.workerSrc ="../../build/browserify/pdf.worker.bundle.js";// Loading a document. const loadingTask = pdfjsLib.getDocument(pdfPath); loadingTask.promise.then(function (pdfDocument) {// Request a first pagereturn pdfDocument.getPage(1).then(function (pdfPage) {// Display page on the existing canvas with 100% scale.const viewport = pdfPage.getViewport({ scale: 1.0 });const canvas = document.getElementById("theCanvas");canvas.width = viewport.width;canvas.height = viewport.height;const ctx = canvas.getContext("2d");const renderTask = pdfPage.render({canvasContext: ctx,viewport,});return renderTask.promise;});}).catch(function (reason) {console.error("Error: " + reason);});代碼不多,使用方式也是很簡單:
- 把 pdf.js 引進來;
- 設置一下worker,在后臺解析 pdf;
- 然后就是加載一個 PDF 文檔;
- 然后就是面向 pdf.js 提供的 API 編程,把內容通過 canvas 渲染展示給用戶(我們平時應該更關心的是這個步驟,中途還會做很多屏幕,縮放的適配,懶加載,內存的回收 …)
- …
一切都很美好,直到遇到:手機QQ,QQ瀏覽器
本來在其他地方已經調試的好好的東西,在這兩個平臺上莫名其妙的掛了,還報了一個錯誤。大致是這樣的:
貼一個文字版的:
n { message: “undefined is not an object (evaluating ‘e.body.getReader’)”, name: “UnknownErrorException”, details: “TypeError: undefined is not an object (evaluating ‘e.body.getReader’)” }
二、問題定位
是不是看著很迷惑,很少見過這種報錯對吧!
這個是從 pdf.js 里面報出來的,其實跟業務代碼沒有關系。那這個時候你可能就想了,既然不是我們的代碼,我們能通過自己的努力解決嗎?
答案是可以的,不然也不會有這個文章!
需要解決這個問題,那就得先了解為什么會出現這個問題。
剛剛也說過,我們在其他瀏覽器是沒有問題的,怎么突然換到 QQ 上就不行了呢?一個念頭閃過:兼容性 !
這個問題必須是兼容性問題。那在定位一下,到底是哪個API出了問題呢?
說實話,這個就不好確定了。這里其實找了很久也沒有確定,就只能跟著報錯的信息一步一步找!直接把 pdf.js 的源碼 clone 下來,自己在源碼里面看!
那既然報錯是:TypeError: undefined is not an object (evaluating ‘e.body.getReader’)
大概就能猜出來:body.getReader 應該是這里出問題了,肯定有一個是 undefined
那就到源代碼里面找:
這里可以大概解釋一下,這里主要是 pdf.js 加載文件的地方,可以看到其實兩個報錯的位置都是在加載文件!
所以聰明的你,一下應該就能猜到答案,兼容性問題就出在這里吧 - Fetch + ReadableStream
這里主要就是使用了 Fetch 加載文檔,然后 pdf.js 從接口中流式讀取內容!
如果不是很熟悉這幾個API的可以看看:
- Fetch
- ReadableStream.getReader()
反復嘗試,確定問題就是出現在這里!
三、解決方案
問題找到了,但是我們可以改變框架的代碼嗎?
答案是不能的,但是我們可以給初始化的邏輯改了。
從 getDocument 入口函數我們可以看到:
pdf.js 是支持我們傳遞一個 pdf 的二進制數據作為初始化的!
我們可以從這里入手。而且也分析過,我們報錯的是因為加載 pdf 文件導致的。所以只要在入口控制住,不讓 pdf.js 走 URL 的方式初始化那就不會出問題!
解決思路就是:我們可以通過 fetch 手動去請求 pdf 文件內容,然后通過 ArrayBuffer 的方式再塞給 pdf.js
這里就是八仙過海各顯神通了!大家可以自己行實現。我把自己的做法貼出來,僅供參考!
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf';let pdfDoc; try {pdfDoc = await pdfjsLib.getDocument(url).promise; } catch (error) {if (String(error).indexOf('response.body.getReader') > -1) {// 某些瀏覽器內核不支持 Fetch - response.body.getReader 的模式,專門在這里修復const pdfData = await fetch(new URL(url, window.location).href,);const arrayBufferPdf = await pdfData.arrayBuffer(); // 轉成 ArrayBuffer 塞給 pdf.jspdfDoc = await pdfLib.getDocument({ data: arrayBufferPdf }).promise;} }// pdf render ...經過以上的代碼,我們在獲取PDF內上,就不會有報錯問題的存在了,后面的渲染邏輯跟以前是一樣的,就不再次贅述!
四、思維拓展
經過了這一番折騰,回過頭來看,其實解決問題的方式很簡單,如果能早一點想到就好了。
在這里,也把自己想到的一個場景記錄下來:展示內存中的PDF文件
大概的意思就是:如果我們想讓用戶隨便選擇一個 pdf 文件,我們給他顯示出來。如果你看懂了上面的解決方案,那這個需求就很容易實現:
- 聲明一個 input 框,類型是 file
- 在 onchang 的時候,如果有內容,就 new FileReader() 初始化一個 Reader,讀取文件內容
- Reader 返回的是一個二進制的內容,我們在轉一下,變成 Uint8Array 再塞給 pdf.js 就解決問題了!
原文地址:www.abners.cn/pdf-error
大家感興趣可以看公眾號:
總結
以上是生活随笔為你收集整理的Pdf.js body.getReader 报错问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【kmp】似乎在梦中见过的样子
- 下一篇: 【论文笔记】基于 VR 的移动机器人真实