pdfjs優化,實現按需載入,節省流量和記憶體
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的報文,就說明開啟了,如下圖所示:
3.3 pdfjs關閉自動獲取
在pdfjs發行包的web/viewer.js檔案中,找到配置項disableAutoFetch,可以看到它的預設值是false,意味著會自動獲取所有分片。
將它改為true,意味著關閉自動獲取,它僅僅會下載所需要的分片,實現了按需載入。
3.4 效果確認
可以看到,除了載入開頭的幾個分片之外(這幾個分片中包含pdf元資料,目錄等),不會再載入其它。只有等到要訪問某個頁面的時候,才會接著發起請求,做到了按需載入。如下圖所示。
4 參考資料
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range
https://mozilla.github.io/pdf