react-pdf 实现pdf文件预览功能
参考文档
https://www.npmjs.com/package/react-pdfhttps://github.com/wojtekmaj/react-pdf#readme
一、概述
react项目中,很多时候(尤其是需展示报告的页面)会遇到需要预览pdf文件的需求。而据调研,使用react-pdf插件可以很好地实现这个功能。
二、操作步骤
1. 安装
1 | yarn add react-pdf 或 npm install --save react-pdf |
2. 新建一个 PdfViewer.jsx 的公共组件,作为pdf预览的封装文件
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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | 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文件预览的页面,进行处理
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 | import React, { useEffect, useState, useRef } from 'react' ;<br> 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){ <br> // setFilePath(filePath); //本地测试文件url<br> // 或 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); }) };<br> 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一般是没有问题的)!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?