React实现预览PDF

622 阅读2分钟

React实现预览PDF

前段时间因为业务需求,要在web端进行PDF预览,后端返回的PDF是Blob格式,本来想着直接用react-pdf,但是按照官网demo操作报了一堆的错误,可能和webpack以及react的版本有关系,由于相关文档资料介绍的太少,最后还是决定使用pdfjs-dist。

首先说一下应用环境,以及相关插件安装的版本!

create-react-app@5.0.0 //脚手架版本react@17.0.2  // react版本
​
pdfjs-dist@2.4.456  // pdfjs-dist版本

其他版本可能会出一些莫名奇妙的错误,可以查询相关文档进行解决,具体实现代码如下,相关包进入如下,具体可根据自己需要进行引入。

import React, { useState, useEffect, useCallback } from 'react';
import { Pagination, Button, Space, Spin } from 'antd';
import { PlusOutlined, MinusOutlined } from '@ant-design/icons';
import Axios from "axios";
import PDFJS from 'pdfjs-dist';
import workerSrc from 'pdfjs-dist/build/pdf.worker.entry'
PDFJS.workerSrc = workerSrc;
let pdfDoc;

下面是具体实现代码。

function PreviewPdf(){
    const [loading, setLoading] = useState(false);//加载动画
    const [pageNum, setPageNum] = useState(1);//当前页码
    const [total, setTotal] = useState(0);//pdf总页数
    const [scale, setScale] = useState(1.5);//pdf缩放比例
     useEffect(() => {
        setLoading(true);
        Axios({
                method: "get",
                url: `url`,
                params: {},
                responseType: 'blob'
            }).then((res) => {
                const blob = new Blob([res.data], {
                    type: "application/pdf;chartset=UTF-8",
                });
                PDFJS.getDocument(URL.createObjectURL(blob)).promise.then((_pdfDoc) => {
                    setLoading(false);
                    pdfDoc = _pdfDoc;//pdfjs实例化对象
                    setTotal(_pdfDoc.numPages);//获得总数
                    renderPages(1, 1.5)//默认设置显示第一页,设置放大倍数为1.5倍
                })
          })
    }, [renderPdf])
    
    const renderPdf = useCallback((currentPageNum,currentScale)=>{
          pdfDoc.getPage(currentPageNum).then((pageContent) => {
            let canvas = document.getElementById("pdf-canvas");
            let vp = pageContent.getViewport({ scale: s });
            let ctx = canvas.getContext('2d')
            let dpr = window.devicePixelRatio || 1
            let bsr = ctx.webkitBackingStorePixelRatio ||
                ctx.mozBackingStorePixelRatio ||
                ctx.msBackingStorePixelRatio ||
                ctx.oBackingStorePixelRatio ||
                ctx.backingStorePixelRatio || 1;
            let ratio = dpr / bsr;
            let viewport = pageContent.getViewport({ scale: window.innerWidth / vp.width });
            canvas.width = viewport.width * ratio
            canvas.height = viewport.height * ratio
            canvas.style.width = viewport.width + 'px'
            ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
            let renderContext = {
                canvasContext: ctx,
                viewport: viewport
            }
            pageContent.render(renderContext)
            setPageNum(currentPageNum)
            setScale(currentScale)
        })
    })
    function handleScaleAdd() {//增大缩放倍数
        renderPdf(pageNum, scale + 0.1);
    }
    function handleScaleSub() {//减小缩放倍数
        if (scale > 0.5) {//小于0.5不再进行缩小
            renderPages(pageNum, scale-0.1);
        }
    }
       const pagination = {
        total,
        current: pageNum,
        pageSize: 1,
        size: "small",
        showSizeChanger: false,
        onChange: (current) => {
            renderPdf(current, scale);
        },
​
    }
    return (
        <div className="pdf-wrap">
            <Spin spinning={loading}>
                <div className="pdf-container">
                    <canvas id="pdf-canvas"></canvas>
                </div>
                <div className="pdf-control">
                    <div className="pdf-control-page">
                        <Pagination {...pagination} />
                    </div>
                    <div className="pdf-control-zoom">
                        <Space>
                            <Button size="small" icon={<PlusOutlined />} onClick={handleScaleAdd} />
                            <Button size="small" icon={<MinusOutlined />} onClick={handleScaleSub} />
                        </Space>
                    </div>
                </div>
            </Spin>
        </div>
    )
}

样式如下

.pdf-wrap {
    background-color: #fff;
    display: flex;
    flex-direction: column;
​
    .pdf-container {
        flex: 1;
        overflow: auto;
        text-align: center;
        background-color: rgba(0, 0, 0, 0.2);
​
        .water-mark {
            display: none;
        }
​
    }
​
    .pdf-control {
        padding:12px;
        height: 50px;
        background: rgba(103, 103, 103, 1);
        display: flex;
        justify-content: space-between;
        color: #fff;
    }
}

\