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