react-pdf 实现pdf文件预览功能
参考文档
https://www.npmjs.com/package/react-pdfhttps://github.com/wojtekmaj/react-pdf#readme
一、概述
react项目中,很多时候(尤其是需展示报告的页面)会遇到需要预览pdf文件的需求。而据调研,使用react-pdf插件可以很好地实现这个功能。
二、操作步骤
1. 安装
yarn add react-pdf 或 npm install --save react-pdf
2. 新建一个 PdfViewer.jsx 的公共组件,作为pdf预览的封装文件
import React, { useEffect, useState, useRef } from 'react'; import { Spin, Tooltip, Input } from 'antd'; import { LeftOutlined, RightOutlined, ZoomInOutlined, ZoomOutOutlined } from '@ant-design/icons'; import { Document, Page, pdfjs } from "react-pdf"; import 'react-pdf/dist/esm/Page/AnnotationLayer.css'; import 'react-pdf/dist/esm/Page/TextLayer.css'; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; const PageCom = (props) => { const [filePath, setFilePath] = useState(null); const [pageCurrent, setPageCurrent] = useState(1); const pageCurrentRef = useRef(pageCurrent); const [pageTotal, setPageTotal] = useState(1); const pageTotalRef = useRef(pageTotal); const [pageWidth, setPageWidth] = useState(960); const pageWidthRef = useRef(pageWidth); useEffect(()=>{ setPageCurrent(1); pageCurrentRef.current= 1; setFilePath(props.filePath); },[props.filePath]) const prevPage = () => { if (pageCurrentRef.current == 1) { return; } setPageCurrent(pageCurrentRef.current - 1); pageCurrentRef.current= pageCurrentRef.current - 1; }; const nextPage = () => { if (pageCurrentRef.current == pageTotalRef.current) { return; } setPageCurrent(pageCurrentRef.current + 1); pageCurrentRef.current= pageCurrentRef.current + 1; }; const pageNumChange = e => { let value = Number(e.target.value); let value2 = 1; if(value<=0){ value2 = 1; } else if(value >= pageTotalRef.current){ value2 = pageTotalRef.current; } else { value2 = value; } setPageCurrent(value); pageCurrentRef.current= value; }; const toPage = e => { console.log('toPage====',e) let value = Number(e.target.value); let value2 = value; if(value<=0){ value2 = 1; } else if(value >= pageTotalRef.current){ value2 = pageTotalRef.current; } else { value2 = value; } setPageCurrent(value2); pageCurrentRef.current= value2; }; const pageZoomOut = () => { if (pageWidthRef.current <= 960) { return } const pageWidth = pageWidthRef.current * 0.8; setPageWidth(pageWidth); pageWidthRef.current = pageWidth; }; const pageZoomIn = () => { const pageWidth = pageWidthRef.current * 1.2 setPageWidth(pageWidth); pageWidthRef.current = pageWidth; }; const onDocumentLoadSuccess = (args) => { setPageTotal(args.numPages); pageTotalRef.current = args.numPages; }; return ( <div className="pdfViewer-wrapper"> {filePath?(<> <div className="pageContainer"> <Document file={filePath} onLoadSuccess={ onDocumentLoadSuccess } loading={<Spin size="large" />} > <Page pageNumber={pageCurrent} width={pageWidth} loading={<Spin size="large" />} /> </Document> </div> <div className="pageTool"> <Tooltip title={pageCurrent == 1 ? "已是第一页" : "上一页"}> <LeftOutlined onClick={prevPage} /> </Tooltip> <Input value={pageCurrent} onChange={ pageNumChange } onPressEnter={ toPage } type="number" /> / {pageTotal} <Tooltip title={pageCurrent == pageTotal ? "已是最后一页" : "下一页"}> <RightOutlined onClick={ nextPage } /> </Tooltip> <Tooltip title="放大"> <ZoomInOutlined onClick={ pageZoomIn } /> </Tooltip> <Tooltip title="缩小"> <ZoomOutOutlined onClick={ pageZoomOut } /> </Tooltip> </div> </>):(<div className="empty-wrapper">未生成报告文件!</div>)} </div> ); }; export default PageCom;
3. 在需要pdf文件预览的页面,进行处理
import React, { useEffect, useState, useRef } from 'react';
import pdfPath from '@/lib/demo.pdf'; import PdfViewer from './PdfViewer.jsx'; import '@/styles/report.less'; import axios from 'axios' const PageCom = (props) => { const [filePath, setFilePath] = useState(null); useEffect(()=>{ if(token){
// setFilePath(filePath); //本地测试文件url
// 或 getData(); //远程获取文件url } },[token]) /* 获取报告文件流数据 */ const getData = ()=>{ axios.get('/api/getPdf', { responseType: 'blob' }).then(res => { const blob = new Blob([res.data], { type: res.headers["content-type"] }); let URL = window.URL || window.webkitURL; let href = URL.createObjectURL(blob); setFilePath(href); }) };
return <div className="page-fjhScreenReport"> {/* 查询条件 */} {/* ... */} {/* 文档内容 */} <div className="con-wrapper"> <PdfViewer filePath={filePath}/> </div> </div>; }; export default PageCom;
4. 对pdf预览样式进行处理
.pdfViewer-wrapper { background:#f5f5f5; display: flex; justify-content: center; height: 100vh; padding: 10px 0; overflow: auto; box-sizing: border-box; .empty-wrapper{ padding: 100px 0 50px; text-align: center; font-size: 20px; letter-spacing: 2px; color: #999; } .pageContainer { z-index: 1; box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 4px 0px; width:max-content; max-width:100%; &:after{ content:''; position: relative; display: block; height:110px; clear:both; overflow: hidden; } } .pageTool{ z-index: 99; font-size: 18px; position: absolute; bottom: 10px; background: rgb(66, 66, 66); color: white; padding: 8px 15px; border-radius: 15px; display: flex; justify-content: center; align-items: center; .anticon{margin: 0 10px;} i{ padding: 5px; margin:0 5px; &:hover{ background: #333; } } input{ display: inline-block; width: 50px; text-align: center; margin-right: 10px; height: 24px; } input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; } input[type="number"]{ -moz-appearance: textfield; } } }
注释:pdf 文件因为字体等原因,可能导致不完全加载,详见参考文档(不过用通用字体生成的pdf一般是没有问题的)!