Implement file import in antdPro in react - implement code and pitfall records

need:

In the web-side background management project, it is necessary to implement the file upload and import function, return import error messages and prompts, and display the imported data in tables.

environment:

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

Problems encountered during the process:

  1. When there is an error in the uploaded file, the backend directly returns the wrong file stream (the response is garbled), which is difficult to retrieve from the frontend. ----Solution: Add configuration when requesting: responseType: 'blob', so that the file stream can be read;
  2. If the responseType is 'blob', then whether the upload is successful or unsuccessful, the blob format will be returned, and it is difficult to distinguish whether the upload is successful or not. ----Solution: Determine whether the res.type is application/json, because blob has a type attribute, and then it is found that the type type is different in success and failure, so the type attribute is judged; you can check your own interface situation To judge success or failure;
  3. It is required to prompt the user for the number of successfully imported data and the number of failed data, but the format returned by the interface is: when successful, {successNum: 2, failNum: 0} will be returned in the response. This can be obtained by reading the blob and then converting it to json. to; but when it fails, the response directly returns the document stream, and the backend cannot wrap a layer outside, so the number of success and failure information is put in the "Content-Disposition" field of the response header. When getting this number, I encountered two problems: ①. The request is using umi's request, and the returned data is directly the back-end data, not the complete response structure of axios, so you need to add configuration to this request separately { getResponse : true } to return the complete response and get the content in the response headers; ②. You can get the Content-Type field in the headers, but you cannot get the "Content-Disposition", and it can be seen in the network response header. to these fields. You need to set up the backend to expose this field before the frontend can get it: response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
  4. There is another problem that has nothing to do with import, but the problem of function parameter passing: the first parameter of the function must be passed, but the second and third parameters are not required. However, I only want to pass the first parameter and the third parameter. Then in the position of the second parameter, you can pass undefined or null.

Code:

1.Import button

If the table component is ProTable, add a button to the toolbar --toolBarRender

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

2.Import method

 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();
    }
  };

If you have any questions, feel free to discuss them with me.

Guess you like

Origin blog.csdn.net/weixin_62192841/article/details/133065100