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;

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

相关推荐
小林ixn3 小时前
从零到一理解 React 父子组件通信:手写一个 Todo 应用带你彻底搞懂单向数据流
前端·javascript·react.js
qetfw6 小时前
MXU:Tauri 2 + React 的 MaaFramework 跨平台 GUI 源码
前端·python·react.js·前端框架·开源项目·效率工具
鱼毓屿御7 小时前
从「只会聊」到「边想边做」的跃迁
前端·学习·react.js
嘟嘟07178 小时前
搞懂 useState 从原生 DOM 到 React 惰性初始化的完整链路
前端·react.js·编程语言
先吃饱再说11 小时前
React 组件设计:从 Props 传递到组件封装
前端·react.js
小月土星14 小时前
React 状态管理核心原理:从批量 DOM 到派生状态
react.js
~烈15 小时前
Acrobat Pro DC 便携精简版 PDF 专业处理工具安装教程
pdf·acrobat pro dc·pdf 专业处理工具
烬羽15 小时前
AI 写代码总翻车?试试"先画图再砌墙"的 Vibe Coding 三步法
react.js·ai编程·vibecoding
我不是QI15 小时前
Master PDF Editor 逆向复现
汇编·安全·pdf
光影少年16 小时前
react navite 页面跳转、传参、路由监听、导航栏自定义
前端·react native·react.js