React Antd Upload組件上傳多個(gè)文件實(shí)現(xiàn)方式
前言
實(shí)現(xiàn)需求:
上傳多個(gè)文件。其實(shí)就是獲取多個(gè)文件的絕對(duì)路徑交給后端接口去處理。
Upload組件
首先,這里不能調(diào)用Upload的onChange函數(shù),因?yàn)樯蟼髦?、完成、失敗都?huì)調(diào)用這個(gè)函數(shù),在多個(gè)文件的情況下會(huì)出現(xiàn)多次調(diào)用問(wèn)題。
改用beforeUpload和customRequest。
import React, { useRef, useState, useEffect } from 'react';
import { UploadOutlined } from '@ant-design/icons';
import type { UploadFile, UploadProps } from 'antd';
import { Button, Upload } from 'antd';
import { useTranslation } from 'react-i18next';
import Console from '../../../../util/Console';
import { UploadChangeParam } from 'antd/es/upload';
import { sendMsgToMain } from 'electron-prokit';
type Props = {
name?: string;
onChangeFilePath: (path: string | null | unknown, info?: UploadChangeParam<UploadFile<any>>) => void;
accept: Array<string>;
headers?: any;
progress?: any;
buttonTitle?: string;
rest?: UploadProps;
action?: string | ((file: any) => Promise<string>);
};
const FileMultiSelectButton: React.FC<Props> = ({
name,
onChangeFilePath,
accept,
headers,
progress = null,
buttonTitle,
action,
...rest
}) => {
const { t } = useTranslation();
const fileState: any = useRef();
const [uploadFiles, setUploadFiles] = useState<UploadFile[]>([]);
const updateFiles = (function () {
let fileList: UploadFile[] | null = null;
return function (list: UploadFile[], setState: React.Dispatch<React.SetStateAction<UploadFile[]>>) {
if (!fileList) {
fileList = list;
setState && setState(list);
}
return {
fileList,
reset() {
fileList = null;
}
};
};
})();
const beforeUpload = (_: any, fileList: UploadFile[]) => {
fileState.current = updateFiles(fileList, setUploadFiles);
return false;
}
const customRequest = () => {
if (uploadFiles.length > 0) {
const path = uploadFiles.map(item => (item as any).path);
// 這是electron的ipc處理函數(shù),在node中復(fù)制文件
sendMsgToMain({ key: 'fileMultiSelectPath', data: path }).then(filePath => {
if (typeof onChangeFilePath === 'function') {
onChangeFilePath(filePath);
} else {
Console.handleErrorMsg('onChangeFilePath is not a function');
}
});
} else {
Console.handleWarningMsg('no file uploaded');
}
}
useEffect(() => {
if (uploadFiles.length > 0) {
customRequest();
fileState.current.reset();
}
}, [uploadFiles]);
return (
<Upload
name={name || 'file'}
accept={accept.join(',')}
progress={progress}
showUploadList={false}
headers={headers}
action={action}
multiple={true}
beforeUpload={beforeUpload}
customRequest={customRequest}
{...rest}
>
<Button icon={<UploadOutlined />}> {buttonTitle || t('Select')} </Button>
</Upload>
);
};
export default FileMultiSelectButton;
在node中處理(不必須),這里主要因?yàn)閑lectron需要兼容不同的平臺(tái),需要處理文件路徑
function generateRandomString(length: number) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
const fileSelectPath = (sourcePath: string) => {
return new Promise((resolve, reject) => {
const userDataPath = app.getPath('userData');
// 獲取用戶數(shù)據(jù)目錄,并在該目錄下創(chuàng)建文件夾,用于保存用戶數(shù)據(jù),需要判斷是否存在,如果存在則直接使用,否則創(chuàng)建,并返回該目錄,用于后續(xù)操作,并需要兼容跨平臺(tái),比如windows和mac
// 拼接出 files 目錄的完整路徑
const filesDirPath = path.join(userDataPath, 'files');
fs.mkdir(filesDirPath, { recursive: true }, err => {
if (err) {
// 如果目錄創(chuàng)建失敗,返回錯(cuò)誤信息
// 如果目錄創(chuàng)建失敗,處理錯(cuò)誤
console.error('Failed to create files directory:', err);
}
// 生成一個(gè)隨機(jī)數(shù)
const randomBytes = generateRandomString(8); // 生成8字節(jié)的隨機(jī)數(shù)
// 獲取當(dāng)前日期和時(shí)間
const date = new Date();
const formattedDate = date.toISOString().split('T')[0].replace(/-/g, ''); // 格式化為不含破折號(hào)的日期
const formattedTime = date.toTimeString().split(' ')[0].replace(/:/g, ''); // 格式化為不含冒號(hào)的時(shí)間
// 提取源文件名和擴(kuò)展名
const { name, ext } = path.parse(sourcePath);
// 根據(jù)日期、時(shí)間和隨機(jī)數(shù)生成一個(gè)新的文件名
const newName = `${name}_${formattedDate}_${formattedTime}_${randomBytes}${ext}`;
// 構(gòu)造目標(biāo)路徑
const destinationPath = path.join(filesDirPath, newName);
// 使用fs模塊的copy方法復(fù)制文件
fs.copyFile(sourcePath, destinationPath, err => {
if (err) {
// 如果文件已存在或復(fù)制失敗,返回錯(cuò)誤信息
reject(err);
} else {
// 復(fù)制成功,返回新的文件路徑
resolve(destinationPath);
}
});
});
});
};
export const fileMultiSelectPath = (sourcePath: Array<string>) => {
return Promise.all(sourcePath.map(async (sourcePath) => {
return await fileSelectPath(sourcePath);
}));
};
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
react 報(bào)錯(cuò)Module build failed: Browserslis
這篇文章主要介紹了react 報(bào)錯(cuò)Module build failed: BrowserslistError: Unknown browser query `dead`問(wèn)題的解決方法,需要的朋友可以參考下2023-06-06
React render核心階段深入探究穿插scheduler與reconciler
這篇文章主要介紹了React render核心階段穿插scheduler與reconciler,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-11-11
淺談對(duì)于react-thunk中間件的簡(jiǎn)單理解
這篇文章主要介紹了淺談對(duì)于react-thunk中間件的簡(jiǎn)單理解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05
React使用useImperativeHandle自定義暴露給父組件的示例詳解
useImperativeHandle?是?React?提供的一個(gè)自定義?Hook,用于在函數(shù)組件中顯式地暴露給父組件特定實(shí)例的方法,本文將介紹?useImperativeHandle的基本用法、常見(jiàn)應(yīng)用場(chǎng)景,需要的可以參考下2024-03-03
React-Native之定時(shí)器Timer的實(shí)現(xiàn)代碼
本篇文章主要介紹了React-Native之定時(shí)器Timer的實(shí)現(xiàn)代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10

