React实现大文件上传、react-dropzone

React大文件上传的实现方案大致如下:

  1. 使用第三方组件库实现文件上传,如react-dropzone。

  2. 将大文件分成多个小块,并使用XMLHttpRequest或者fetch发送分块上传请求。为了保证数据完整性,每个请求都需要携带校验码。在上传过程中需要实时统计上传进度。

  3. 后端接收到分块上传的请求后,将每个分块存储到指定路径下,并根据校验码判断分块是否完整。

  4. 当所有分块上传成功后,后端将分块整合成一个完整的文件,并删除已上传的分块。

  5. 当文件上传成功后,前端清除上传状态并显示成功的提示信息。

下面是一个基于react-dropzoneaxios实现的React大文件上传组件代码:

import React, { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import axios from 'axios';

const LargeFileUploader = () => {
  const [uploadedFiles, setUploadedFiles] = useState([]);

  const onDrop = async (acceptedFiles) => {
    // 使用FormData来封装文件数据
    const formData = new FormData();
    acceptedFiles.forEach((file) => {
      formData.append('files', file);
    });

    try {
      // 发起上传请求
      const response = await axios.post('/upload-url', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
        onUploadProgress: (progressEvent) => {
          // 上传进度回调
          const progress = Math.round(
            (progressEvent.loaded / progressEvent.total) * 100
          );
          console.log(`Upload Progress: ${progress}%`);
        },
      });

      // 上传成功后更新已上传文件列表
      setUploadedFiles(response.data);
      console.log('Upload Successful!');
    } catch (error) {
      console.error('Upload Error:', error);
    }
  };

  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

  return (
    <div {...getRootProps()} className="dropzone">
      <input {...getInputProps()} />
      {isDragActive ? (
        <p>拖放文件到此处以上传</p>
      ) : (
        <p>将文件拖放至此处,或点击选择文件进行上传</p>
      )}
      <ul>
        {uploadedFiles.map((file, index) => (
          <li key={index}>{file.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default LargeFileUploader;

        在上面的代码中,我们创建了一个名为LargeFileUploader的组件。它使用react-dropzone来实现文件拖放和选择功能,并使用axios发起上传请求。

        在onDrop方法中,我们首先将接收到的文件数据封装到FormData对象中,然后使用axios.post方法发送POST请求到指定的上传URL。我们设置请求头的Content-Typemultipart/form-data以支持文件上传。

        在请求配置中,我们还通过onUploadProgress参数传入一个回调函数,用于监视文件上传的进度并显示在控制台中。

        上传成功后,我们将服务器返回的已上传文件列表更新到uploadedFiles状态,并在页面中渲染出来。

        最后,在渲染部分,我们使用useDropzonehook获取所需的属性和事件绑定,并根据拖放状态显示相应提示信息和已上传文件列表。

        需要注意的是,示例中的上传URL路径/upload-url需要替换为自己的后端上传接口路径。另外,还需要对上传接口进行额外的处理和验证,以适应后端逻辑。

但是在文件比较大的情况下需要将文件切片上传,可以在使用react-dropzoneaxios的基础上进行扩展 ,在原有的基础上添加ChunkedFileUploader组件,并引入一个常量CHUNK_SIZE来指定切片大小(这里设置为1MB):

import React, { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import axios from 'axios';

const CHUNK_SIZE = 1024 * 1024; // 切片大小为1MB

const ChunkedFileUploader = () => {
  const [uploadedChunks, setUploadedChunks] = useState({});
  const [uploadedFile, setUploadedFile] = useState(null);
  const [isUploading, setIsUploading] = useState(false);

  const onDrop = (acceptedFiles) => {
    const file = acceptedFiles[0];
    setUploadedFile(file);
  };

  const uploadChunks = async () => {
    if (!uploadedFile) return;

    setIsUploading(true);

    try {
      const totalChunks = Math.ceil(uploadedFile.size / CHUNK_SIZE);
      const chunksToUpload = [];

      // 分割文件为切片
      for (let i = 0; i < totalChunks; i++) {
        const start = i * CHUNK_SIZE;
        const end = Math.min(start + CHUNK_SIZE, uploadedFile.size);
        const chunk = uploadedFile.slice(start, end);
        chunksToUpload.push(chunk);
      }

      // 依次上传切片
      for (let i = 0; i < chunksToUpload.length; i++) {
        const formData = new FormData();
        formData.append('file', chunksToUpload[i]);
        formData.append('chunkNumber', i + 1);
        formData.append('totalChunks', totalChunks);

        await axios.post('/upload-url', formData, {
          headers: {
            'Content-Type': 'multipart/form-data',
          },
        });

        setUploadedChunks((prevChunks) => ({
          ...prevChunks,
          [i + 1]: true,
        }));
      }

      console.log('Upload Complete!');
    } catch (error) {
      console.error('Upload Error:', error);
    } finally {
      setIsUploading(false);
    }
  };

  const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });

  return (
    <div {...getRootProps()} className="dropzone">
      <input {...getInputProps()} />
      {isDragActive ? (
        <p>拖放文件到此处以上传</p>
      ) : (
        <p>将文件拖放至此处,或点击选择文件进行上传</p>
      )}
      {uploadedFile && !isUploading && (
        <button onClick={uploadChunks}>上传</button>
      )}
      {isUploading && <p>上传中...</p>}
      <ul>
        {Object.keys(uploadedChunks).map((chunkNumber) => (
          <li key={chunkNumber}>{`切片 ${chunkNumber}`}</li>
        ))}
      </ul>
    </div>
  );
};

export default ChunkedFileUploader;

        在上传时,根据文件大小计算出切片的总数。然后,我们通过循环将文件分割成多个切片,并使用FormData对象将每个切片进行封装。最后,通过循环依次上传每个切片,并在每个切片上传成功后更新已上传切片的状态。 

猜你喜欢

转载自blog.csdn.net/weixin_40381947/article/details/131450834
今日推荐