这个东西做的我真的是头昏脑涨
主要需求是,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;
写的可能还有些需要优化的,但是我已经累了,暂时先这样吧