react的pdf转图片格式上传到后端

这个东西做的我真的是头昏脑涨

主要需求是,upload上传pdf,pdf转图片格式展示,以图片格式上传到后端

封装了组件代码

父组件直接放就可以了

使用的插件pdfjs-dist,版本是 "pdfjs-dist": "2.5.207",

javascript 复制代码
import React, { useState, useEffect, useRef } from 'react';
import { Spin, Pagination } from 'antd';
import { Form, UploadProps, Image } from "antd";
import { message, Modal, Upload, Button } from "antd";
import { useDispatch, useSelector } from 'react-redux';
import type RootState from '@/types/rootState';
import { getToken } from "@/utils/login";
import config from "@/utils/config";
import type { RcFile, UploadChangeParam } from "antd/lib/upload";
import type { UploadFile } from "antd/es/upload/interface";
import { Toast } from "antd-mobile";
import { LoadingOutlined, PlusOutlined } from "@ant-design/icons";
import type { PurchaseOrderAction } from "@/pages/purchase/purchaseOrder/model";
var pdfjsLib = require('pdfjs-dist/build/pdf.js');
import 'pdfjs-dist/build/pdf.worker.entry';

interface PdfViewerProps {
    url: string;
}
const formItemLayout = {
    labelCol: {
        span: 3,
    },
    wrapperCol: {
        span: 12,
    },
};
const PdfViewer = () => {
    const [pageNumbers, setPageNumbers] = useState(1);
    const [loading, setLoading] = useState(true);
    const [supportImages, setSupportImages] = useState<string[]>([]);
    const [uploadingImages, setUploadingImages] = useState<boolean>(false);
    const [supportFileList, setSupportFileList] = useState<UploadFile[]>([]);
    const [previewImage, setPreviewImage] = useState<string>('');
    const [previewVisible, setPreviewVisible] = useState<boolean>(false);
    const dispatch = useDispatch();
    const pdfRef: any = useRef(null);
    const processPage = async (pageNumbers: any, pdfFile: any, pdfName: any, uid: any, type: any) => {
       //  转base64
        let canvas: any = document.getElementById('pdf');
        let canvasContext = canvas.getContext('2d');
        const page = await pdfFile.getPage(pageNumbers);
        const viewport = page.getViewport({ scale: 5 });
        canvas.height = viewport.height;
        canvas.width = viewport.width;
        canvasContext.clearRect(0, 0, canvas.width, canvas.height);

        const renderContext = {
            canvasContext: canvasContext,
            viewport: viewport,
            rotate: 90,
        };

        await page.render(renderContext).promise;
        // 转base64
        const imgUrl = canvas.toDataURL('image/jpg');
        const thumbimg = dataURLtoFile(imgUrl, 'image/png');
        const fileOfBlob = new File([thumbimg], `${pdfName}.png`);

        const formData = new FormData();
        formData.append('file', fileOfBlob, `${pdfName}.png`);
        formData.append('dir', 'purchase');
        
        const response = await fetch(config.api.pictureUpload, {
            method: 'POST',
            headers: {
                'x-access-token': getToken(),
            },
            body: formData,
        });

        if (response.ok) {
            const data = await response.json();
            return {
                uid: uid,
                name: `${pdfName}.png`,
                status: 'done',
                url: data.data,
            };
        }
        return null;
    }
   //  pdf转图片
    const processAllPages = async (numpage: any, pdfFile: any, pdfName: any, uid: any, type: any) => {
        const arr: any = [];
        const pdfImgArr:any = []
       //  pdf可能不止一个页面,循环提取
        for (let i = 1; i < numpage; i++) {
            const imgData = await processPage(i, pdfFile, pdfName, uid, type);
            if (imgData) {
                arr.push(imgData);
                pdfImgArr.push(imgData.url)
            }
        }
        setUploadingImages(false);
       //  因为我需要跟别的接口上传的图合并 ,所以做了存储
        dispatch<PurchaseOrderAction>({
            type: 'purchaseOrder/updateState',
            payload: {pdfImgArr},
        })
        
        setSupportFileList(arr);
    };
    const uploadProps: UploadProps = {
        action: config.api.pictureUpload,
        headers: {
            'x-access-token': getToken()!,
        },
        data: { dir: 'purchase' },
        listType: 'picture-card',
        showUploadList: false,
    };
    //  生成图片格式
    function dataURLtoFile(dataURI: any, type: any) {
        let binary = atob(dataURI.split(',')[1]);
        let array = [];
        for (let i = 0; i < binary.length; i++) {
            array.push(binary.charCodeAt(i));
        }
        return new Blob([new Uint8Array(array)], { type: type });
    }
   //  上传的文件 
    const handleUploadSupportImages = (file: UploadChangeParam<UploadFile<any>>) => {
        let name = file.file.name.split('.')[0]
        let uid = file.file.uid
        let type = 'image/png'
        const reader = new FileReader()
        reader.readAsArrayBuffer(file.file.originFileObj)
        reader.onload = function () {
            const pdfFile = reader.result;
            let loadingTask = pdfjsLib.getDocument(pdfFile);
            loadingTask.promise
                .then((pdf: any) => {
                    setPageNumbers(pdf.numPages);
                    let numpage = Number(pdf.numPages) + 1
                    if (numpage > 9) {
                        Toast.fail("超过数量限制!");
                        return
                    }
                    processAllPages(numpage, pdf, name, uid, type);
                })
                .catch((error: any) => {
                    console.log(error);
                })
                .finally(() => {
                    setLoading(false);
                });

        }
        if (file.file.status === 'uploading') {
            setUploadingImages(true);
            return;
        }
        if (file.file.status === 'done') {
            const { response } = file.file;
            supportImages.push(response.data);
            setSupportImages(supportImages);
            if (response.success) {
                dispatch<PurchaseOrderAction>({
                    type: 'deliveryDetail/updateState',
                    payload: {},
                });
                setUploadingImages(false);
            } else {
                message.error(response.message);
                setUploadingImages(false);
            }
        }

    };
    //预览
    const handlePreview = (file: UploadFile<any>) => {
        setPreviewImage(file.url! || file.thumbUrl!)
        setPreviewVisible(true)
    }
    const uploadImagesButton = (
        <div>
            {uploadingImages ? <LoadingOutlined /> : <PlusOutlined />}
            <div style={{ marginTop: 8 }}>上传pdf</div>

        </div>
    );
    const handleCancelPreview = () => {
        setPreviewVisible(false)
    }
    return (
        <>
            <Form.Item
                {...formItemLayout}
                label="导入pdf文件"
                hasFeedback
                style={{ marginTop: '15px' }}
            >
                <Upload
                    {...uploadProps}
                    showUploadList={true}
                    multiple={true}
                    accept='.pdf'
                    maxCount={1}
                    onPreview={handlePreview}
                    onChange={(info) => {
                        handleUploadSupportImages(info)
                    }}
                    showUploadList={{showRemoveIcon:false}}
                    fileList={supportFileList}
                >
                    {uploadImagesButton}
                </Upload>
            </Form.Item>

            <Modal
                open={previewVisible}
                title='图片预览'
                footer={null}
                onCancel={handleCancelPreview}
            >
                <img alt="example" style={{ width: '100%' }} src={previewImage} />
            </Modal>
            <canvas id="pdf" style={{ width: '0px' }} />
        </>
    );
};

export default PdfViewer;

写的可能还有些需要优化的,但是我已经累了,暂时先这样吧

相关推荐
FØund4047 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
join81 小时前
解决vue-pdf的签章不显示问题
javascript·vue.js·pdf
小行星1251 小时前
前端把dom页面转为pdf文件下载和弹窗预览
前端·javascript·vue.js·pdf
疯狂的沙粒1 小时前
如何在 React 项目中应用 TypeScript?应该注意那些点?结合实际项目示例及代码进行讲解!
react.js·typescript
鑫宝Code2 小时前
【React】React Router:深入理解前端路由的工作原理
前端·react.js·前端框架
沉默璇年11 小时前
react中useMemo的使用场景
前端·react.js·前端框架
红绿鲤鱼12 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
loey_ln14 小时前
FIber + webWorker
javascript·react.js
zhenryx15 小时前
前端-react(class组件和Hooks)
前端·react.js·前端框架
穆友航15 小时前
PDF内容提取,MinerU使用
数据分析·pdf