寫在最前
因項目原因,需涉及到pdf在線瀏覽技術,但是少數情況下由於pdf文件過大,會導致系統加載緩慢,影響用戶體驗。因此,實現pdf分頁瀏覽可有效的提高在線瀏覽速度。
技術棧為:SpringBoot、Vue、pdfjs、pdfbox等。
主要核心思路:前端請求時請求頭附帶請求范圍range及讀取大小,后端根據請求頭返回相應的pdf文件流
現存問題:單頁面數據大小無法估量,導致分片大小無法更好的設置(DEFAULT_RANGE_CHUNK_SIZE 值),分頁查看出現問題(如:excel轉pdf后單頁面數據量大導致單頁大小為xxM,此時分頁單詞請求大小必須大於這個值才可實現分頁查看)
后端實現
注:將以下方法作為工具類,然后傳入實際的pdf文件對象即可,控制層不需要做多余處理
涉及依賴
- common-io:便捷操作IO流,非必選
<!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency>
分頁方法
/** * @Date:2022/2/10 14:00 * @Author:lngrid 分頁加載pdf */ public static void loadPDFByPage(File file, HttpServletResponse response, HttpServletRequest request) { BufferedInputStream bis = null; OutputStream os = null; BufferedOutputStream bos = null; InputStream is = null; try { is = new FileInputStream(file); bis = new BufferedInputStream(is); os = response.getOutputStream(); bos = new BufferedOutputStream(os); // 下載的字節范圍 int startByte, endByte, totalByte; if (request != null && request.getHeader("range") != null) { // 斷點續傳 String[] range = request.getHeader("range").replaceAll("[^0-9\\-]", "").split("-"); // 文件總大小 totalByte = is.available(); // 下載起始位置 startByte = Integer.parseInt(range[0]); // 下載結束位置 if (range.length > 1) { endByte = Integer.parseInt(range[1]); } else { endByte = totalByte - 1; } // 返回http狀態 response.setStatus(206); } else { // 正常下載 // 文件總大小 totalByte = is.available(); // 下載起始位置 startByte = 0; // 下載結束位置 endByte = totalByte - 1; // 返回http狀態 response.setHeader("Accept-Ranges", "bytes"); response.setStatus(200); } // 需要下載字節數 int length = endByte - startByte + 1; // 響應頭 response.setHeader("Accept-Ranges", "bytes"); response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + totalByte); // response.setContentType("application/pdf"); response.setContentType("application/octet-stream"); response.setContentLength(length); // 響應內容 bis.skip(startByte); int len = 0; byte[] buff = new byte[1024 * 64]; while ((len = bis.read(buff, 0, buff.length)) != -1) { if (length <= len) { bos.write(buff, 0, length); break; } else { length -= len; bos.write(buff, 0, len); } } } catch (IOException e) { e.printStackTrace(); } finally { //也可使用try catch關閉IO流 IOUtils.closeQuietly(bos); IOUtils.closeQuietly(os); IOUtils.closeQuietly(bis); IOUtils.closeQuietly(is); } }
前端實現
1、引入pdfjs
http://mozilla.github.io/pdf.js/getting_started/#download
2、解壓文件並存放至vue項目的public路徑下,並將文件夾更名為pdfjs,方便后續調用
3、修改pdfjs配置項(重點)
pdfjs支持pdf分頁操作,無需單獨再行添加js方法,
首先,找到viewer.js,搜索disable關鍵詞,在13968行開始的配置
修改為如下:(注釋說明為個人結合網絡資料及個人理解添加,非官方說明),重點為 disableAutoFetch、disableStream 均改為true,網上資料只修改第一個就行了,但始終不成功,也不知道為啥
"disableAutoFetch": true, //是否禁用自動獲取,true為禁用自動獲取,開啟分頁 "disableFontFace": false, "disableRange": false, //是否禁用range獲取文件,false表示支持分頁請求頭 "disableStream": true, //分頁關鍵,是否禁用流的形式加載
然后,找到build/pdf.js文件,尋找 DEFAULT_RANGE_CHUNK_SIZE 配置項,並修改為 65536*16
4、前后端請求調試
前端使用iframe標簽加載pdf文件,訪問路徑為
/public/pdfjs/web/viewer.html?file=后端請求鏈接
常見問題
- disableStream:按照參考資料來講,不需要修改此配置,所以也未曾嘗試修改此配置,結果一直沒有成功,后來修改后意外發現成功了,然后就是搜索此配置的實際作用,但仍未了解這個配置的實際作用。以下為網絡上的一些參考描述:
- DEFAULT_RANGE_CHUNK_SIZE的問題:在實際操作時忽略了這個參數的配置,認為也影響不大,但是發現如果為默認值每次請求的大小很小,導致雖然有多個分頁請求,但是pdf文件卻無法成功加載,故嘗試修改為*16后,發現單次請求響應大小為1M,pdf也能成功加載了。
參考鏈接
-
Lazy load :How to display multiple pdf documents as one with pdf.js?
-
Getting disableAutoFetch resp. disableStream working... #7937