最新的PDF.js实现按需加载文章:SpringBoot+PDF.js实现按需分片加载预览(包含可运行示例源码)
前言:
主要解决大体积pdf在线浏览加载缓慢,影响用户体验的问题
技术栈为:SpringBoot、Vue、pdfjs
主要核心思路:前端请求时请求头附带请求范围range及读取大小,后端根据请求头返回相应的pdf文件流
后端实现
@GetMapping("/load")
public void loadPDFByPage(HttpServletResponse response, HttpServletRequest request,
@RequestParam("dataId") long dataId,
@RequestParam("archiveSid") String archiveSid) {
//dataId, archiveSid参数只是为了获取到pdf的File对象
File pdf = pdfReadService.getArchive(dataId, archiveSid);
try (InputStream is = new FileInputStream(pdf);
BufferedInputStream bis = new BufferedInputStream(is);
OutputStream os = response.getOutputStream();
BufferedOutputStream 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");
//Content-Range: bytes 0-65535/408244,表明此次返回的文件范围
response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + totalByte);
//告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载
response.setContentType("application/octet-stream");
//表明该文件的所有字节大小
response.setContentLength(length);
//需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,因此会认为服务器端不支持分片,所以会直接全文下载
response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");
// 响应内容
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();
}
}
注意事项
1.首次加载返回状态码200,注意以下属性服务器端在响应头中务必要加上
//表明服务器支持分片加载
response.setHeader("Accept-Ranges", "bytes");
//Content-Range: bytes 0-65535/408244,表明此次返回的文件范围
response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + totalByte);
//告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载
response.setContentType("application/octet-stream");
//表明该文件的所有字节大小
response.setContentLength(length);
//需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,因此会认为服务器端不支持分片,所以会直接全文下载
response.setHeader("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");
2.之后每次请求都会返回206,即已经实现分片加载。Content-Range: bytes 0-65535/408244,表明此次返回的文件范围
遇到的问题
前端的同学在配置完成PDF.js后,调用始终是一个请求加载了全部文件,并未分片加载
首先服务端响应头返回Accept-Ranges: bytes,就表明服务器端支持分片加载,pdf.js就会自动启用分片加载文件的策略。由于浏览器默认只允许js读取以下的响应头,而Accept-Ranges: bytes是不支持的,这就造成pdf.js读取Accept-Ranges时,读到了null值,认为你的服务器不支持分片,故整个文件下载了。
解决方法:在服务端的返回响应头上增加:
'Access-Control-Expose-Headers':'Accept-Ranges,Content-Range'
前端实现
我搞后端滴,前端自己看下边的文档
https://www.cnblogs.com/ingrid/articles/15886403.html