SpringBoot预览PDF文件
关键点说明
预览与下载的优先级
Content-Disposition: inline
要求浏览器优先内联显示(预览)。- 若浏览器不支持预览(如某些移动端),会自动触发下载。
性能与安全
- 流式传输(非一次性加载到内存)支持大文件。
- 校验文件存在性避免无效请求。
兼容性
- 文件名编码处理确保中文等特殊字符正常显示。
SpringBoot预览
1 |
|
HTTP Range 请求
HTTP 协议支持 Range
请求头,允许客户端请求文件的某一部分(字节范围),服务器可以响应 206 Partial Content
并返回指定范围的数据。这种方式非常适合大文件的懒加载。
实现步骤
客户端:在请求头中添加
Range
字段,例如:1
Range: bytes=0-1023
表示请求文件的前 1024 字节。
服务器:
- 解析
Range
请求头,获取请求的字节范围。 - 使用
RandomAccessFile
或FileChannel
读取文件的指定部分。 - 返回
206 Partial Content
状态码,并在响应头中设置Content-Range
。
- 解析
响应头:
1
2
3HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/123456
Content-Length: 1024
代码示例
1 |
|
前端实现懒加载
在前端,可以通过以下方式实现 PDF 的懒加载
使用 PDF.js(Mozilla 开源的 PDF 渲染库):
PDF.js 支持分块加载和渲染,适合大文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<html>
<head>
<title>滚动加载 PDF</title>
<style>
.page-container {
margin: 20px auto;
box-shadow: 0 0 5px #ccc;
}
</style>
</head>
<body>
<div id="pdf-container"></div>
<!-- 引入 PDF.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script>
<script>
const url = 'http://127.0.0.1:8080/pdfpreview2'; // 你的 PDF 接口地址
let currentPage = 1; // 当前加载的页数
let pdfInstance = null; // 保存 PDF 实例
let isRendering = false; // 防止重复渲染
// 初始化 PDF 加载
const loadPDF = async () => {
try {
const loadingTask = pdfjsLib.getDocument({
url: url,
rangeChunkSize: 65536, // 分块大小(64KB)
disableAutoFetch: true, // 禁用自动加载整个文件
});
pdfInstance = await loadingTask.promise;
renderPage(currentPage); // 渲染第一页
addScrollListener(); // 添加滚动监听
} catch (error) {
console.error("PDF 加载失败:", error);
}
};
// 渲染单个页面
const renderPage = async (pageNumber) => {
if (isRendering || pageNumber > pdfInstance.numPages) return;
isRendering = true;
try {
const page = await pdfInstance.getPage(pageNumber);
const viewport = page.getViewport({ scale: 1.5 });
// 创建画布容器
const container = document.createElement('div');
container.className = 'page-container';
container.style.width = `${viewport.width}px`;
container.style.height = `${viewport.height}px`;
document.getElementById('pdf-container').appendChild(container);
// 创建画布
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
container.appendChild(canvas);
// 渲染页面
await page.render({
canvasContext: context,
viewport: viewport,
}).promise;
console.log(`第 ${pageNumber} 页渲染完成`);
currentPage++;
isRendering = false;
// 立即检查是否需要加载下一页
checkScroll();
} catch (error) {
console.error(`第 ${pageNumber} 页渲染失败:`, error);
isRendering = false;
}
};
// 检查是否滚动到底部
const checkScroll = () => {
const lastPageContainer = document.querySelector('.page-container:last-child');
if (!lastPageContainer) return;
const lastPageBottom = lastPageContainer.offsetTop + lastPageContainer.offsetHeight;
const scrollPosition = window.innerHeight + window.scrollY;
// 如果距离底部小于 100px,加载下一页
if (scrollPosition > lastPageBottom - 100) {
renderPage(currentPage);
}
};
// 添加滚动监听
const addScrollListener = () => {
window.addEventListener('scroll', checkScroll);
};
// 启动 PDF 加载
loadPDF();
</script>
</body>
</html>
总结
springboot-demo/pdf_lazy_loading at main · fulsun/springboot-demo
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 凉月の博客!
评论