微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 KendoReact Upload 组件将文件转换为 base64 和异步状态更新

如何解决使用 KendoReact Upload 组件将文件转换为 base64 和异步状态更新

我正在尝试使用 KendoReact Upload 组件制作可重复使用的多文件上传组件。我所拥有的是功能性的,但我对 React 还很陌生,所以希望有一种更简洁的方法来实现这一点。

认情况下,该组件采用上传/删除请求端点的 URL,但 saveUrl / removeUrl 道具也可以采用允许操作上传文件函数。我正在使用 FileReader 将文件转换为 base64 并采用一个函数,该函数可在文件上传后用于外部状态更新。

我有以下实现,但感觉很重,因为我需要

  • Kendo 控件使用的文件状态的 UploadFileInfo 集合
  • 状态中的 IFileUploadviewmodel 集合,用于传递包含 base64 内容文件
  • 一个 useEffect 调用来触发外部状态更新,以确保传递更新的状态
  • saveRequestPromise 和 removeRequestPromise 中的函数状态更新以保持控件和外部文件集合同步
import React,{ useEffect,useState } from "react";
import { Upload,UploadFileInfo,UploadOnAddEvent,UploadOnProgressEvent,UploadOnRemoveEvent,UploadOnStatusChangeEvent } from "@progress/kendo-react-upload";

export interface IFileUploadviewmodel {
    uid: string;
    fileName: string;
    fileSize: number;
    content?: string | undefined;
}

export function KendoMultipleSmallFileUpload(props: {
    allowedExtensions?: string[] | undefined;
    minFileSizeMb?: number | undefined;
    maxFileSizeMb?: number | undefined;
    onFileChange: (files: Array<IFileUploadviewmodel>) => void;
}) {
    // The Upload component accepts files from users only,an initial 'system' state e.g. pre-selected file determined by a viewmodel is not a valid use case.
    const [files,setFiles] = useState(new Array<UploadFileInfo>());
    const [fileUploads,setFileUploads] = useState(new Array<IFileUploadviewmodel>());

    useEffect(() => {
        console.log(fileUploads);
        props.onFileChange(fileUploads);
    },[fileUploads]);

    const convertMbToBytes = (mb: number) => mb * 1024 * 1024;
    // The Upload component does not allow empty files,convert file size restrictions to bytes and set a minimum value if not provided.
    const minFileSizeBytes = props.minFileSizeMb ? convertMbToBytes(props.minFileSizeMb) : 1;
    const maxFileSizeBytes = props.maxFileSizeMb ? convertMbToBytes(props.maxFileSizeMb) : undefined;

    // Fires when user clicks on the Remove button while the file upload is in progress. Can be used when the saveUrl option is set to a function that cancels custom requests.
    // For small files,conversion to a byte array is so quick it is impossible to Cancel through the UI and in any case is automatically followed by Remove so no implementation is required.
    // For large files we may need to consider aborting the upload process through the FileReader API.
    function onCancel(event: any) {}

    function onAdd(event: UploadOnAddEvent) {
        setFiles(event.newState);
    }

    function onRemove(event: UploadOnRemoveEvent) {
        setFiles(event.newState);
    }

    function onProgress(event: UploadOnProgressEvent) {
        setFiles(event.newState);
    }

    function onStatusChange(event: UploadOnStatusChangeEvent) {
        setFiles(event.newState);
    }

    function onSaveRequest(
        files: UploadFileInfo[],options: { formData: FormData; requestOptions: any },onProgress: (uid: string,event: ProgressEvent<EventTarget>) => void
    ): Promise<{ uid: string }> {
        const currentFile = files[0] as UploadFileInfo;
        const uid = currentFile.uid;
        console.log("onSaveRequest for " + currentFile.name);

        const saveRequestPromise = new Promise<{ uid: string }>(async (resolve,reject) => {
            // as currently configured it is impossible to request a save from the UI if there are validation errors,but that can be changed so keeping this check in place
            if (currentFile.validationErrors && currentFile.validationErrors.length > 0) {
                reject({ uid: uid });
            } else {
                const reader = new FileReader();
                // onload is executed when the load even is fired i.e. when content read with readAsArrayBuffer,readAsBinaryString,readAsDataURL or readAsText is available
                reader.onload = () => {
                    if (reader.result && typeof reader.result === "string") {
                        // stripping the data-url declaration as per https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL
                        const base64Result = reader.result.split(",")[1];
                        // update viewmodel and resolve
                        const fileUpload: IFileUploadviewmodel = { uid: uid,fileName: currentFile.name,fileSize: currentFile.size!,content: base64Result };
                        setFileUploads((state) => [...state,fileUpload]);

                        resolve({ uid: uid });
                    } else {
                        reject({ uid: uid });
                    }
                };
                // onprogress is fired periodically as the FileReader reads data and the ProgressEvent can be passed directly to the Upload control,handy!
                reader.onprogress = (data) => {
                    onProgress(uid,data);
                };
                // if the read is not completed due to error or user intervention,reject
                reader.onabort = () => {
                    reject({ uid: uid });
                };
                reader.onerror = () => {
                    reject({ uid: uid });
                };

                reader.readAsDataURL(currentFile.getRawFile!());
            }
        });

        return saveRequestPromise;
    }

    function onRemoveRequest(files: UploadFileInfo[],options: { formData: FormData; requestOptions: any }): Promise<{ uid: string }> {
        const currentFile = files[0] as UploadFileInfo;
        const uid = currentFile.uid;

        const removeRequestPromise = new Promise<{ uid: string }>((resolve) => {
            const updatedFileUploads = fileUploads.filter((f) => f.uid !== uid);
            setFileUploads(updatedFileUploads);
            props.onFileChange(updatedFileUploads);
            resolve({ uid: uid });
        });

        return removeRequestPromise;
    }

    return (
        <>
            <Upload
                className="mb-2"
                autoUpload={true}
                batch={false}
                files={files}
                multiple={true}
                onAdd={onAdd}
                onCancel={onCancel}
                onProgress={onProgress}
                onRemove={onRemove}
                onStatusChange={onStatusChange}
                withCredentials={false}
                removeUrl={onRemoveRequest}
                saveUrl={onSaveRequest}
                restrictions={{ allowedExtensions: props.allowedExtensions,minFileSize: minFileSizeBytes,maxFileSize: maxFileSizeBytes }}
            />
            <div className="d-flex flex-row flex-wrap small">
                {props.minFileSizeMb && (
                    <div className="d-flex flex-row mr-2 mb-2">
                        <span className="label font-weight-bold mr-1">Minimum File Size: </span>
                        <span>{props.minFileSizeMb} MB.</span>
                    </div>
                )}
                {props.maxFileSizeMb && (
                    <div className="d-flex flex-row mr-2 mb-2">
                        <span className="label font-weight-bold mr-1">Maximum File Size: </span>
                        <span>{props.maxFileSizeMb} MB.</span>
                    </div>
                )}
                {props.allowedExtensions && (
                    <div className="d-flex flex-row mr-2 mb-2">
                        <span className="label font-weight-bold mr-1">Supported File Types: </span>
                        <span>{props.allowedExtensions.join(",")}.</span>
                    </div>
                )}
            </div>
        </>
    );
}

如有任何建议,我们将不胜感激。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。