React项目中的PDF展示解决方案
本文将梳理两种方案,涉及两个库react-pdf
和pdf.js
,使用环境为ant-design-pro脚手架创建的项目。
使用iframe标签
1、不需要任何依赖的两种方式为:
- 直接使用iframe标签展示pdf文件
其中fileUrl为需要展示pdf的文件地址,宽高和style均可自定义,下不赘述。<iframe src={fileUrl} width={'100%'} height={'100%'} style={{ minHeight: '80vh' }} />
- 如果用户可以访问google,则可以使用如下地址:
其中fileUrl为需要展示pdf的文件地址,宽高和style均可自定义。<iframe src={`http://docs.google.com/gview?url=${fileUrl}&embedded=true`} width={'100%'} height={'100%'} style={{ minHeight: '80vh' }} />
2、使用编译好的静态库pdf.js
的viewer.html展示
编译好的pdfjs可直接下载,也可去官网下载
需要先将编译好的pdfjs
版本放在react
项目的public
目录下
├── config # umi 配置,包含路由,构建等配置
├── mock # 本地模拟数据
├── public # 将编译好的PDFJS放在这个目录下
│ └── favicon.png # Favicon
├── src
│ ├── assets # 本地静态资源
│ ├── components # 业务通用组件
│ ├── e2e # 集成测试用例
│ ├── layouts # 通用布局
│ ├── models # 全局 dva model
│ ├── pages # 业务页面入口和常用模板
│ ├── services # 后台接口服务
│ ├── utils # 工具库
│ ├── locales # 国际化资源
│ ├── global.less # 全局样式
│ └── global.ts # 全局 JS
├── tests # 测试工具
├── README.md
└── package.json
如下图所示
在需要展示PDF文件的地方,使用如下src即可。
<iframe
src={`pdfjs-2.2.228-dist/web/viewer.html?file=${fileUrl}`}
width={'100%'}
height={'100%'}
style={{ minHeight: '80vh' }}
/>
使用react-pdf展示
import { Pagination } from 'antd';
import React, { useState } from 'react';
import { Document, Page } from 'react-pdf/dist/esm/entry.webpack';
// @ts-ignore
const PdfViewer: React.FC<{ url: string }> = ({ url }) => {
const [numPages, setNumPages] = useState(0);
const [pageNumber, setPageNumber] = useState(1);
// @ts-ignore
const onDocumentLoadSuccess = (props: any) => {
setNumPages(props?.numPages);
};
const onChangePage = (page: any) => {
setPageNumber(page);
};
return (
<div>
<div style={{ marginBottom: 4 }}>
<a href={url} target={'_blank'} rel="noreferrer">
查看当前文件
</a>
</div>
<Document file={url} onLoadSuccess={onDocumentLoadSuccess}>
<Page pageNumber={pageNumber} />
</Document>
<Pagination
style={{ marginTop: 4, display: 'flex', justifyContent: 'flex-end' }}
total={numPages}
simple
hideOnSinglePage
showTotal={(total) => `共 ${total} 页`}
current={pageNumber}
pageSize={1}
size="small"
onChange={onChangePage}
/>
</div>
);
};
export default PdfViewer;
pdfjs的另一种使用方式
import { useEffect, useRef, useState } from 'react';
const PDFJsViewer = ({ pdfUrl }: any) => {
const pdfjsLib = require('pdfjs-dist/build/pdf.js');
const pdfjsWorker = require('pdfjs-dist/build/pdf.worker.entry.js');
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;
const canvasRef = useRef(null);
const [scale, setScale] = useState(1);
const [offset, setOffset] = useState({ x: 0, y: 0 });
const handleWheel = (e: any) => {
if (e.deltaY < 0) {
setScale((prevScale) => Math.min(prevScale + 0.1, 3));
} else {
setScale((prevScale) => Math.max(prevScale - 0.1, 0.5));
}
};
const handleMouseDown = (e: any) => {
const initialX = e.clientX - offset.x;
const initialY = e.clientY - offset.y;
const handleMouseMove = (e: any) => {
const newX = e.clientX - initialX;
const newY = e.clientY - initialY;
setOffset({ x: newX, y: newY });
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
useEffect(() => {
const canvas = canvasRef.current;
//@ts-ignore
const ctx = canvas.getContext('2d');
const loadingTask = pdfjsLib.getDocument(pdfUrl);
loadingTask.promise.then((pdf: any) => {
pdf.getPage(1).then((page: any) => {
const viewport = page.getViewport({ scale: scale });
//@ts-ignore
canvas.height = viewport.height;
//@ts-ignore
canvas.width = viewport.width;
ctx.setTransform(1, 0, 0, 1, 0, 0);
//@ts-ignore
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setTransform(scale, 0, 0, scale, offset.x, offset.y);
const renderContext = {
canvasContext: ctx,
viewport: viewport,
};
page.render(renderContext);
});
});
}, [pdfUrl, scale, offset]);
return <canvas ref={canvasRef} onWheel={handleWheel} onMouseDown={handleMouseDown}></canvas>;
};
export default PDFJsViewer;