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的報文,就說明開啟了,如下圖所示:

clip_image002

3.3 pdfjs關閉自動獲取

  在pdfjs發行包的web/viewer.js文件中,找到配置項disableAutoFetch,可以看到它的默認值是false,意味着會自動獲取所有分片。

clip_image004

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

3.4 效果確認

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

clip_image006

4 參考資料

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

//mozilla.github.io/pdf.js/