当前位置: 首页 > article >正文

react中,使用antd的Upload组件切片上传.zip文件及压缩包的下载

页面效果

 

 

JS 

 import type { TableProps, UploadProps } from 'antd';
 import http from '@utils/http';
 import type { UploadProps } from 'antd';

 const [showUploadModal, setShowUploadModal] = useState<boolean>(false);
 const [percent, setPercent] = useState(0);
 const [showProgressBar, setShowProgressBar] = useState(false); 
 const [isDownloading, setIsDownloading] = useState(false); // 正在下载模板
 const isCancelUpload = useRef<boolean>(false); // 是否取消上传

// 模板下载
 const handleExport = async () => {
    try {
      setIsDownloading(true);
      templateDownload()
        .then((res) => {
          console.log('templateDownload res=>', res);
          if (res.type === 'application/zip') {
            const b = new Blob([res], { type: res.type });
            let a: HTMLAnchorElement | null = document.createElement('a');
            a.href = URL.createObjectURL(b);
            a.download = '模板.zip';
            a.click();
            a = null;
          }
        })
        .catch((err: ErrorObj) => {
          showErrorMsg(err);
        })
        .finally(() => {
          setIsDownloading(false);
        });
    } catch (error) {
      setIsDownloading(false);
      console.error('/log/export ~ error:', error);
      showErrorMsg({ msg: '模板下载失败,请重试!' });
    }
  };

// 辅助函数:检查文件是否为zip格式
  function isZipFile(file: File): boolean {
    const fileType = file.type;
    return fileType === 'application/zip' || file.name.endsWith('.zip');
  }

  // 分片大小,单位字节
  const CHUNK_SIZE = configData?.sliceSize * 1024 * 1024;  // configData?.sliceSize是后端返回的切片大小值,存储在Store中

  // 创建分片
  const createChunks = (file: File) => {
    const chunks = [];
    for (let i = 0; i < file.size; i += CHUNK_SIZE) {
      chunks.push(file.slice(i, Math.min(i + CHUNK_SIZE, file.size)));
    }
    return chunks;
  };

  const uploadFileInChunks = async (file: File) => {
    const chunks = createChunks(file);
    console.log('chunks=>', chunks);
    setShowProgressBar(true);
    let fileldId = '';
    setPercent(0);
    try {
      for (let i = 0; i < chunks.length; i++) {
        // 如果弹窗关闭了,则不继续上传,并提示用户上传取消
        if (!isCancelUpload.current) {
          message.error('已取消上传');
          return;
        }

        const formData = new FormData();
        if (i !== 0) {  // 第一次上传不用传fileldId字段
          formData.append('fileId', fileldId);
        }
        formData.append('partIndex', i.toString());
        formData.append('partSize', chunks[i].size.toString());
        formData.append('totalParts', chunks.length.toString());
        formData.append('totalSize', file.size.toString());
        formData.append('partFile', new File([chunks[i]], file.name, { type: file.type })); // 需要把Blob格式转成File格式,不然后端收到的partFile值为空

        // eslint-disable-next-line
        const resFileldId = (await http.request({
          method: 'post',
          url: `/***/file/slice-upload`,  // 上传接口地址
          data: formData,
        })) as any;
        fileldId = resFileldId;
        if (typeof resFileldId === 'string') {
          if (i === chunks.length - 1) {
            setPercent(100);
            message.success('上传成功');
            searchByFilter();  // 请求列表数据
          } else {
            setPercent(Math.floor(((i + 1) / chunks.length) * 100));
          }
        }
      }
    } catch (error: any) {
      console.log('error=>', error);
      message.error(error.msg);
      setPercent(0);
      setShowProgressBar(false);
      searchByFilter();
    }
  };

  const props: UploadProps = {
    name: 'file',
    multiple: false,
    action: ``, // 因为是自定义上传逻辑,所以action值就不需要了
    accept: '.zip',
    showUploadList: false,
    disabled: percent !== 100 && showProgressBar, // 上传进行中,禁用上传组件
    onChange(info) {
      console.log('info=>', info);
    },

    beforeUpload(file) {
      const isZip = isZipFile(file);
      const maxSize = 200 * 1024 * 1024;
      if (!isZip) {
        message.error('请上传一个压缩包文件。');
      } else if (file.size > maxSize) {
        message.error('文件大小不能超过200兆。');
      }

      if (isZip && file.size <= maxSize) {
        uploadFileInChunks(file); // 自定义上传逻辑
      }

      return false; // 禁止组件默认的上传逻辑
    },
  };

HTML

   <Modal
        open={showUploadModal}
        maskClosable={false}
        footer={null}
        width={500}
        onCancel={() => {
          setShowUploadModal(false);
          isCancelUpload.current = false;
          setPercent(0);
          setShowProgressBar(false);
          setIsDownloading(false);
        }}
        centered
      >
        <Spin spinning={isDownloading} tip="正在下载">
          <div className={style.content}>
            <Dragger {...props}>
              <p className={style.upload_icon}>
                <CloudUploadOutlined />
              </p>
              <p>
                <span className={style.tip_txt1}>将文件拖到此处,或</span>
                <Typography.Link disabled={percent !== 100 && showProgressBar}>点击上传</Typography.Link>
              </p>
              <p>
                <span className={style.tip_txt2}>文件详情,请</span>
                <Typography.Link
                  onClick={(event) => {
                    event.stopPropagation();
                    handleExport();
                  }}
                >
                  查看模板
                </Typography.Link>
              </p>
              {showProgressBar && (
                <div>
                  <Progress size={'small'} percent={percent} />
                  <div className={style.red_txt}>注意:关闭弹窗、刷新页面将导致上传失败,需要再次上传。</div>
                </div>
              )}
            </Dragger>
            <div className={style.footer_txt}>
              请将所有文件以一个压缩包的形式上传,支持*.zip文件格式,文件不大于200MB,需要上传随路信息文件、控制文件、音频文件。音频文件支持主流格式如mp4、wav等。
            </div>
          </div>
        </Spin>
</Modal>

CSS

.content {
  padding: 25px;
  .upload_icon {
    font-size: 36px;
    color: #adadb0;
  }
  .tip_txt1 {
    color: #8a8a8c;
  }

  .red_txt {
    color: red;
    font-size: 12px;
    text-align: left;
  }
  .footer_txt {
    margin: 10px 0;
    font-size: 12px;
    color: #6c6c6c;
  }
}


http://www.kler.cn/a/511966.html

相关文章:

  • Spring自定义BeanPostProcessor实现bean的代理Java动态代理知识
  • 嵌入式硬件篇---PID控制
  • 力扣hot100之螺旋矩阵
  • Python爬取豆瓣图书网Top250 实战
  • MATLAB基础应用精讲-【优化算法】阿基米德优化算法(附MATLAB代码实现)
  • 【机器学习实战】kaggle 欺诈检测---使用生成对抗网络(GAN)解决欺诈数据中正负样本极度不平衡问题
  • PyTorch使用教程(15)-常用开源数据集简介
  • 【STM32-学习笔记-15-】MAX7219点阵屏模块
  • Redis - General - 未授权访问漏洞(用户配置问题)
  • Quartus:开发使用及 Tips 总结
  • 适合快递驿站出库仪一体机的安卓主板
  • 如何在龙蜥 OS(AliOS)上安装极狐GitLab?
  • canvas snake game
  • 面向CTF的python_requests库的学习笔记
  • 二十项零信任相关的前沿和趋势性技术-Extranet as a Service
  • 中国综合算力指数(2024年)报告汇总PDF洞察(附原数据表)
  • C#集合排序指南:掌握自定义排序的多种方法
  • 汇编学习笔记(自用)
  • LLM(3) : 浏览器录制16K的音频并上传到后端
  • UG NX二次开发(C#)-创建三维直线段并倒圆
  • 研1如何准备才能找到大厂实习?
  • Sudo命令的配置及使用
  • 【前端】CSS学习笔记(1)
  • Unity自学之旅01
  • JupyterLab 安装以及部分相关配置
  • WSL 2 自动更新 虚拟 IP 到 window hosts