pdfjs優化,實現按需載入,節省流量和記憶體

何德海發表於2020-07-02

1 問題

  當使用pdfjs來實現預覽功能的時候,遇到了2個問題:

  一是頻寬佔用過大,會下載整個pdf檔案,這對部署在公網的應用來說,成本壓力很大,因為雲服務頻寬是很貴的。

  二是記憶體佔用過大,一個80M的pdf,在預覽時佔用記憶體高達600M,在一些記憶體較小的手機上容易發生崩潰。

  pdfjs預設配置下,會載入所有的分片(內容),即使只預覽一個頁面也會載入整個檔案。能不能實現按需載入呢?只載入所預覽的頁面?答案是可以,下面我就詳細地介紹如何做。

2 測試環境

pdfjs 1.10.100 prebuild

chrome 76

springboot 2.1

3 步驟

3.1 原理

  要實現按需下載,需要用到HTTP協議的範圍(Range)請求。MSN站點中有關Range的介紹如下:

The Range HTTP request header indicates the part of a document that the server should return. Several parts can be requested with one Range header at once, and the server may send back these ranges in a multipart document. If the server sends back ranges, it uses the 206 Partial Content for the response. If the ranges are invalid, the server returns the 416 Range Not Satisfiable error. The server can also ignore the Range header and return the whole document with a 200 status code.

  這段文字的大概意思是,客戶端使用Range請求頭,可以要求服務端返回文件的某個部分。如果服務端不支援,則響應200狀態碼並直接返回整個文件的內容。如果服務端支援,則在響應中使用206狀態碼並返回部分內容。

Range示例:
Range: bytes=200-1000
Range: bytes=0-499, -500

  在HTTP伺服器上,當它支援Range請求頭時,也就實現了所謂的“分片下載”、“斷點續傳”功能。為行文的方便,下面都使用’分片下載’這個術語。

3.2 HTTP伺服器啟用分片下載功能

  伺服器要啟用功能,springboot web預設開啟了這個功能,不需要再額外配置。

  如果使用其它的技術棧,一定要確保開啟這個功能!這是必要條件。

  那如何測試HTTP伺服器是否開啟了分片?可以使用chrome開發者模式來確認,如果看到有很多狀態碼為206的報文,就說明開啟了,如下圖所示:

clip_image002

3.3 pdfjs關閉自動獲取

  在pdfjs發行包的web/viewer.js檔案中,找到配置項disableAutoFetch,可以看到它的預設值是false,意味著會自動獲取所有分片。

clip_image004

  將它改為true,意味著關閉自動獲取,它僅僅會下載所需要的分片,實現了按需載入。

3.4 效果確認

  可以看到,除了載入開頭的幾個分片之外(這幾個分片中包含pdf後設資料,目錄等),不會再載入其它。只有等到要訪問某個頁面的時候,才會接著發起請求,做到了按需載入。如下圖所示。

clip_image006

4 參考資料

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range

https://mozilla.github.io/pdf.js/

相關文章