pdf 预览实现
基本步骤
1、安装 pdfjs-dist
npm install pdfjs-dist
2、pdf 文档加载与渲染
import React, { useEffect, useRef, useState } from "react";
import { pdfjs } from "pdfjs-dist";
const PDFViewer = ({ url }) => {
const [pdfDoc, setPdfDoc] = useState(null);
const [pageNum, setPageNum] = useState(1);
const [totalPages, setTotalPages] = useState(0);
const canvasRef = useRef(null);
useEffect(() => {
const loadPdf = async () => {
const loadingTask = pdfjs.getDocument(url);
const pdf = await loadingTask.promise;
setPdfDoc(pdf);
setTotalPages(pdf.numPages);
renderPage(pageNum, pdf);
};
loadPdf();
}, [url]);
const renderPage = async (num, pdf) => {
const page = await pdf.getPage(num);
const viewport = page.getViewport({ scale: 1.5 });
const canvas = canvasRef.current;
const context = canvas.getContext("2d");
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = {
canvasContext: context,
viewport: viewport,
};
page.render(renderContext);
};
const handleNextPage = () => {
if (pageNum < totalPages) {
setPageNum((prevPageNum) => {
const newPage = prevPageNum + 1;
renderPage(newPage, pdfDoc);
return newPage;
});
}
};
const handlePrevPage = () => {
if (pageNum > 1) {
setPageNum((prevPageNum) => {
const newPage = prevPageNum - 1;
renderPage(newPage, pdfDoc);
return newPage;
});
}
};
return (
<div>
<canvas ref={canvasRef}></canvas>
<div>
<button onClick={handlePrevPage} disabled={pageNum <= 1}>
Previous Page
</button>
<button onClick={handleNextPage} disabled={pageNum >= totalPages}>
Next Page
</button>
<p>
Page {pageNum} of {totalPages}
</p>
</div>
</div>
);
};
export default PDFViewer;
移动端兼容性调整
在移动端展示 PDF 时,需要确保界面适应小屏幕,并提供适当的交互功能,如缩放、滑动等。可以使用 CSS 媒体查询调整布局,并结合 react-pinch-zoom 或手动实现缩放功能。
移动端的 CSS 媒体查询
canvas {
width: 100%;
height: auto;
max-width: 100%;
}
@media only screen and (max-width: 768px) {
canvas {
width: 100%;
}
}
使用 react-pinch-zoom 实现缩放和移动
- 安装
npm install react-pinch-zoom
- 使用该库处理移动端的缩放操作
import PinchZoomPan from "react-pinch-zoom";
const PDFViewerMobile = () => {
return (
<PinchZoomPan minScale={1} maxScale={3}>
<canvas ref={canvasRef}></canvas>
</PinchZoomPan>
);
};
常见问题
文件过大
卡顿和滚动延迟
部分设备性能不佳
优化策略
渐进式加载
按需加载页面
懒加载其他页面
PDF文件压缩
分页加载
CDN
缓存优化
PDF预览其他方案
使用 <embed>
、 <iframe>
、<object>
标签直接嵌入 PDF
<embed src="path/to/file.pdf" type="application/pdf" width="100%" height="600px" />
<!-- 或 -->
<iframe src="path/to/file.pdf" width="100%" height="600px"></iframe>
<!-- 或 -->
<object data="./test.pdf" type="application/pdf" width="100%" height="100%"></object>
特点
<embed>
和<iframe>
标签直接利用浏览器内置的 PDF 渲染功能来显示 PDF 文件。不同的浏览器有不同的 PDF 渲染引擎,例如 Chrome 使用 PDFium,Firefox 使用 PDF.js。
这种方式的优势在于无需额外的代码库,但浏览器的渲染效果取决于其内置的 PDF 查看器支持,可能在部分设备或浏览器上不一致。
PDF.js
pdf.js分为3层:
Core Layer(核心层):pdf.worker.js,用于解析和解释二进制PDF文档,这一层是所有后续层的基础。一般不会直接操作核心层,而是去操作由核心层封装的展示层。
Display Layer(展示层):pdf.js,是对核心层进行了一个封装,从而得到更容易使用的API,用来展示PDF或从文档获取其他信息。
Viewer Layer(查看器层):viewer.html+viewer.css+viewer.js,构建在显示层上,是PDF查看器的UI。
// 引入 PDF.js 库
import { pdfjsLib } from 'pdfjs-dist';
const url = 'path/to/file.pdf';
pdfjsLib.getDocument(url).promise.then((pdf) => {
pdf.getPage(1).then((page) => {
const viewport = page.getViewport({ scale: 1.5 });
const canvas = document.getElementById('pdf-canvas');
const context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({ canvasContext: context, viewport: viewport });
});
});
特点
PDF.js 是 Mozilla 开发的一个用 JavaScript 编写的 PDF 渲染引擎。它将 PDF 文件转换为 HTML5 Canvas 可理解的内容,允许直接在浏览器中渲染 PDF。
PDF.js 会读取 PDF 文件的二进制数据,解析文件中的 PDF 内容(包括文本、图像、字体等),将其渲染成 Canvas 图像。
PDF 解析步骤:
pdfjsLib.getDocument() 会异步获取并解析 PDF 文件。
pdf.getPage() 获取 PDF 的具体页面。
page.render() 方法将页面内容渲染到 Canvas 上。Canvas 是一种 HTML5 绘图元素,支持位图渲染,适合显示复杂图形内容。
底层渲染过程
- PDF.js 中的解析器将 PDF 文件的二进制数据转化为 JSON 格式的抽象图形指令。
- 使用 Canvas API 渲染文本和图像,这些图形指令会转换为低层次的绘制命令,在 Canvas 上渲染 PDF 内容。
第三方PDF查看库,如React-pdf
// React-pdf 示例
import { Document, Page } from 'react-pdf';
<Document file="path/to/file.pdf">
<Page pageNumber={1} />
</Document>
特点
React-pdf 基于 PDF.js,将 PDF 文件的内容解析并渲染为 React 组件,提供更好的框架兼容性。
底层依赖与 PDF.js 相同,通过 PDF.js 的 API 获取 PDF 页面的内容并渲染到 Canvas。
React-pdf 提供了一些额外的功能和组件封装,使得在 React 项目中集成更为方便。
PDF转换为图像格式预览
使用后端工具或脚本将PDF文件的每一页转换为图像格式,在前端加载这些图像
from pdf2image import convert_from_path
pages = convert_from_path('path/to/file.pdf', 300)
for page in pages:
page.save('page.png', 'PNG')
特点
PDF 转图像:PDF 文件通过后端工具逐页渲染为图像。PDF 格式本质上是矢量格式,通过图像转换将其转为位图格式。
前端直接显示图像,避免了浏览器 PDF 兼容问题。
缺点:图像格式的 PDF 失去了交互性(无法搜索、复制文本),且图像文件通常较大,加载性能可能受到影响。
使用外部PDF查看服务
上传 PDF 到 Google Drive 或 OneDrive,获取公开的 URL,然后嵌入到 <iframe>
中。
<iframe src="https://drive.google.com/file/d/your-file-id/preview" width="100%" height="600px"></iframe>
特点
将 PDF 文件托管在外部服务上,利用它们的 PDF 查看功能。
前端只是加载外部的 PDF 预览页面,无需自行渲染。
缺点:文件需要上传至第三方,不适用于敏感或私密的 PDF 文件。
使用canvas绘制PDF内容
手动解析 PDF 文件内容并在 Canvas 上绘制。通常不直接使用,而是借助 PDF.js 等库实现。
Canvas 提供了绘图 API,可以渲染文本、图像、路径等内容。
特点
Canvas 是一种位图绘图方式,用于将 PDF 内容逐帧渲染在浏览器上。
JavaScript 中的 Canvas API 允许我们直接操控像素内容,但需要处理复杂的 PDF 解析工作。
不适合手动实现解析 PDF,因为 PDF 内容复杂,推荐用 PDF.js 等库来简化开发。