Bootstrap

antd Upload 自定义上传

需求

由于使用antd 上传图片,在某些情况下,例如token 失效,导致上传图片错误,但是antd 组件始终会走onChange 事件
导致上传路径为空情况

解决办法

使用自定义上传

 // 自定义上传图片
    const customRequest = params => {
        const { file, onSuccess, onError } = params;
        const formData = new FormData();
        formData.append('upload_file', file);
        Resource.handleUploadPicture(formData)
            .then(ret => {
                if (ret?.XCmdrCode === 0) {
                    if (typeof setPictureList === 'function') {
                        const temFile = {
                            ...ret?.XCmdrResult,
                        };
                        onSuccess(ret); // 上传成功的图片会显示绿色的对勾
                        let newFiles = [...fileListRef.current, temFile];
                        if (!multiple) {
                            // 只保留最后一个文件
                            newFiles = newFiles.slice(-1);
                        }
                        setPictureList(_, newFiles);
                    }
                } else {
                    message.error(`${ret?.XCmdrMessage}`);
                }
            })
            .catch(ret => {
                onError();
            });
    };


// 图片属性配置
    const pictureProps = {
        action: `https://admin-api.zhgcloud.com/attachments?token=${token}`,
        name: 'upload_file',
        // action: `https://admin-api.zhgcloud.com/images?token=${token}`,
        // name: 'machine_image',
        listType: 'picture-card',
        fileList,
        multiple,
        ...res,
        beforeUpload: onBeforeUpload || defaultBeforeUpload,
        onChange: ({ file, fileList, event }) => {
            // let newFiles = [...fileList];
            // if (!multiple) {
            //     // 只保留最后一个文件
            //     newFiles = newFiles.slice(-1);
            // }
            // if (typeof setPictureList === 'function') {
            //     setPictureList(file, newFiles);
            // }
        },
        customRequest,
        onRemove: file => {
            const freshFiles = fileList?.filter(ele => ele.uid !== file.uid);
            if (typeof onRemove === 'function') {
                onRemove(file, freshFiles);
            }
        },
        onPreview: async file => {
            if (!file.url && !file.preview) {
                file.preview = await getBase64(file.originFileObj);
            }
            setUrl(file?.url || file?.response?.XCmdrResult?.url || file.previe);
        },
        onDownload: onDownload || defaultOnDownload,
    };

完整组件逻辑

import React, { useEffect, useState, useRef, forwardRef, useImperativeHandle, onBeforeUpload } from 'react';
import { PlusOutlined } from '@ant-design/icons';
import { Upload, Modal, message } from 'antd';
import { saveAs } from 'file-saver';
import { useMount, useUnmount } from 'ahooks';
import FileViewer from '@/components/FileViewer';
import useToken from '@/hooks/useToken';
import Resource from '@/services/Resource';

const MAX_FILE_SIZE = 8;
const UNIT = 1024 * 1024;
function getBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = () => resolve(reader.result);
        reader.onerror = error => reject(error);
    });
}
const createFileObject = file => {
    const fileObject = {
        lastModified: file.lastModified,
        lastModifiedDate: file.lastModifiedDate,
        name: file.name,
        size: file.size,
        type: file.type,
        uid: file.uid,
    };
    return fileObject;
};
const defaultOnDownload = file => {
    // if (file?.url || file?.response?.XCmdrResult?.url) {
    //     window.open(file.url, '_blank');
    // }
    const url = file?.url || file?.response?.XCmdrResult?.url;
    saveAs(url, `${file?.name || file?.url || ''}`);
};
const defaultBeforeUpload = file => {
    const fileType = ['jpg', 'jpeg', 'png'];
    // 文件显示
    return new Promise((resolve, reject) => {
        const curType = file.name.substr(file.name.lastIndexOf('.') + 1).toLowerCase();
        if (!fileType.includes(curType)) {
            Modal.warning({
                maskClosable: true,
                title: '图片格式须为jpg、jpeg、png!',
            });
            // reject(file);
            return Upload.LIST_IGNORE;
        } else if (file.size > MAX_FILE_SIZE * UNIT) {
            Modal.warning({
                maskClosable: true,
                title: `单个图片大小不能超过${MAX_FILE_SIZE}M!`,
            });
            return Upload.LIST_IGNORE;
        } else {
            resolve(file);
        }
    });
};
const UploadPicture = ({
    refChildPicture,
    limit = 3,
    action,
    onRemove,
    setPictureList = () => {},
    onDownload,
    pictures = [],
    isPaste = false,
    multiple = true,
    outlinedName = '上传图片',
    ...res
}) => {
    const [fileList, setFileList] = useState([]);
    const [url, setUrl] = useState('');
    const token = useToken();
    const fileListRef = useRef([]);
    useEffect(() => {
        fileListRef.current = [...pictures];
        setFileList([...pictures]);
    }, [pictures]);
    const saveFiles = (file, ret) => {
        //             // 图片转换成base64编码作为缩略图
        const reader = new FileReader();
        // 监听图片转换完成
        reader.addEventListener(
            'load',
            () => {
                // ...接入antd upload 的 filelist 中
                const temFile = {
                    ...file,
                    response: ret,
                    originFileObj: createFileObject(file),
                    id: new Date().getTime(),
                    status: 'done',
                    name: ret?.XCmdrResult?.path || file.name,
                    url: ret?.XCmdrResult?.url,
                    thumbUrl: reader.result,
                };
                if (typeof setPictureList === 'function') {
                    let newFiles = [...fileListRef.current, temFile];
                    if (!multiple) {
                        // 只保留最后一个文件
                        newFiles = newFiles.slice(-1);
                    }
                    setPictureList(_, newFiles);
                }
            },
            false,
        );
        reader.readAsDataURL(file);
    };
    // 自定义上传图片
    const customRequest = params => {
        const { file, onSuccess, onError } = params;
        const formData = new FormData();
        formData.append('upload_file', file);
        Resource.handleUploadPicture(formData)
            .then(ret => {
                if (ret?.XCmdrCode === 0) {
                    onSuccess();
                    saveFiles(file, ret);
                } else {
                    message.error(`${ret?.XCmdrMessage}`);
                }
            })
            .catch(ret => {
                onError();
            });
    };
    // 图片属性配置
    const pictureProps = {
        action: `https://admin-api.zhgcloud.com/attachments?token=${token}`,
        name: 'upload_file',
        // action: `https://admin-api.zhgcloud.com/images?token=${token}`,
        // name: 'machine_image',
        listType: 'picture-card',
        fileList,
        multiple,
        ...res,
        beforeUpload: onBeforeUpload || defaultBeforeUpload,
        onChange: ({ file, fileList, event }) => {
            // let newFiles = [...fileList];
            // if (!multiple) {
            //     // 只保留最后一个文件
            //     newFiles = newFiles.slice(-1);
            // }
            // if (typeof setPictureList === 'function') {
            //     setPictureList(file, newFiles);
            // }
        },
        customRequest,
        onRemove: file => {
            const freshFiles = fileList?.filter(ele => ele.uid !== file.uid);
            if (typeof onRemove === 'function') {
                onRemove(file, freshFiles);
            }
        },
        onPreview: async file => {
            if (!file.url && !file.preview) {
                file.preview = await getBase64(file.originFileObj);
            }
            setUrl(file?.url || file?.response?.XCmdrResult?.url || file.previe);
        },
        onDownload: onDownload || defaultOnDownload,
    };
    useImperativeHandle(refChildPicture, () => {
        // return返回的值就可以被父组件获取到
        return {
            getFileData() {
                return {
                    fileList,
                };
            },
        };
    });
    useMount(() => {
        if (!isPaste) {
            return;
        }
        const haoroomsbox = document.getElementById('ant-upload-picture-area-id');
        haoroomsbox.addEventListener('paste', event => {
            const data = event.clipboardData || window.clipboardData;
            const items = data.items;
            let tempFile = null; // 存储文件数据
            if (items && items.length) {
                // 检索剪切板items
                for (let i = 0; i < items.length; i++) {
                    if (items[i].type.indexOf('image') !== -1) {
                        tempFile = items[i].getAsFile();
                        break;
                    }
                }
            }
            window.willUploadPictureList = tempFile;
            event.preventDefault();
            submitUpload(tempFile);
        });
    });
    const submitUpload = file => {
        if (!file) {
            return;
        }
        if (fileListRef.current && fileListRef.current.length >= limit) {
            message.error(`上传最大限制数量为${limit}`);
            return;
        }
        const formData = new FormData();
        formData.append('upload_file', file);

        Resource.handleUploadPicture(formData)
            .then(ret => {
                if (ret?.XCmdrCode === 0) {
                    saveFiles(file, ret);
                } else {
                    message.error(`${ret?.XCmdrMessage}`);
                }
            })
            .catch(ret => {
                message.error('上传失败');
            });
    };
    useUnmount(() => {
        const haoroomsbox = document.getElementById('ant-upload-picture-area-id');
        if (haoroomsbox) {
            // haoroomsbox.removeEventListener('paste', () => {});
        }
    });
    return (
        <div ref={refChildPicture} id="ant-upload-picture-area-id">
            <Upload {...pictureProps}>
                {fileList && fileList.length >= limit ? null : (
                    <div>
                        <PlusOutlined />
                        <div>{outlinedName}</div>
                    </div>
                )}
            </Upload>
            <FileViewer url={url} onCancel={() => setUrl('')} />
        </div>
    );
};

export default forwardRef(UploadPicture);

;