react中antdPro里实现文件导入--实现代码及踩坑记录

需求:

web端后台管理项目中,需要实现文件上传导入功能,并返回导入错误信息及提示、表格展示导入的数据。

环境:

react@17 + Ant Design [email protected] + [email protected]

过程中遇到的问题:

  1. 当上传的文件有错误时,后端直接返回了错误的文件流(response是乱码),前端不好取。----解决:在请求时添加配置:responseType: 'blob',这样才可以读出文件流;
  2. 如果 responseType 是 'blob' 的话,那么不管是上传成功或是不成功,都是返回的blob格式,不好区分是否上传成功。----解决:判断的res.type是不是为 application/json ,因为blob有type属性,然后发现,成功和失败时的,type类型不同,所以判断的这个type属性;大家可以看自己接口情况去判断成功失败哈;
  3. 要求提示用户成功导入的数据条数和失败的条数,但是接口返回的格式是:成功时,会在response中返回 { successNum:2,failNum:0 } ,这可以通过读blob,然后转json拿到;但是失败时,response直接返回了文档流,且后端不好在外面包一层,所以就把成功失败的信息数放在了响应头的“Content-Disposition”字段里拼上的。在拿这个数的时候,遇到了两个问题:①.请求是用的umi的request,返回的数据直接就是后端的数据,并不是axios 完整的 response 结构,所以需要单独在这个请求加配置 { getResponse: true } ,才能返回完整response,才能拿到response headers中的内容;②.在headers中可以get到Content-Type字段,但是get不到“Content-Disposition”,且network的响应头里都能看到这些字段。需要后端设置暴露一下这个字段,前端才可以拿到:response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
  4. 还有一个问题跟导入没关系,是函数传参的问题:函数第一个参数必传,第二三个参数非必传,但是,我只想传第一个参数和第三个参数。那么在第二个参数的位置,传undefined或者null都行。

代码实现:

1.导入按钮

表格组件时ProTable,则在工具栏添加按钮--toolBarRender

 toolBarRender={() => [
                <Upload
                  beforeUpload={beforeUpload}
                  accept=".xls,.xlsx"
                  showUploadList={false}
                  key="push"
                >
                  <Button type="primary">导 入</Button>
                </Upload>,
              ]}

2.导入方法

 const beforeUpload = async (file: RcFile) => {
    //文件格式及大小的校验
    const isExcel =
      file.type === 'application/vnd.ms-excel' ||
      file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
    if (!isExcel) {
      message.error('文件格式不正确!');
      return;
    }
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isLt2M) {
      message.error('上传的文件不要超过2M');
      return;
    }
    //给后端发请求----添加responseType及getResponse配置
    const res = await api.transportation.uploadUsingPOST({ file }, undefined, {
      responseType: 'blob',
      getResponse: true,
    });
    //判断是否全部导入成功还是有错误数据
    if (res.data.type !== 'application/json') {
      //获取响应头中拼接的成功条数和失败条数数据
      const headerContent = res.response.headers?.get('Content-Disposition')?.split(';');
      const successNum = headerContent?.[2].split('=')[1];
      const failNum = headerContent?.[3].split('=')[1];
    //如果成功0条,直接提示导入失败
      if (successNum == 0) {
        notification.error({
          message: '数据导入失败',
          description: `${failNum} 条数据导入失败,正在下载错误信息文件`,
          placement: 'top',
        });
      } else {
    //如果有成功的,则刷新表格,提示部分失败
        actionRef.current?.reload();
        notification.warning({
          message: '部分数据导入失败',
          description: `${successNum} 条数据导入成功;\n${failNum} 条数据导入失败,正在下载错误信息文件`,
          placement: 'top',
        });
      }

      //如果后台返回流,说明有数据导入失败,则自动下载导入失败数据的excel表
      const link = document.createElement('a');
      const blob = new Blob([res.data as Blob], { type: 'application/x-excel' });
      link.style.display = 'none';
      link.href = URL.createObjectURL(blob);
      const filename = '导入失败数据.xlsx';
      link.download = filename;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    } else {
      //如果全部导入成功,则读取blob,拿到返回数据,提示用户
      const reader = new FileReader();
      reader.readAsText(res.data as Blob, 'utf-8');
      reader.onload = () => {
        const data = JSON.parse(reader.result); //这里转json格式
        notification.success({
          message: '数据导入成功',
          description: `已成功导入 ${data['成功条数']} 条数据`,
          placement: 'top',
        });
      };
      actionRef.current?.reload();
    }
  };

如果有任何问题,欢迎与我讨论。

猜你喜欢

转载自blog.csdn.net/weixin_62192841/article/details/133065100