You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ReactJS中重复上传CSV解析时出现Out of Memory Error问题

问题描述

使用PapaParse将CSV文件文本转换为JSON,点击按钮上传CSV并转为对象数组的功能初次运行正常,但重复执行上传流程时会触发内存溢出错误(Out of Memory Error)。

相关代码

文件上传处理函数

changeHandlerForCsvUpload(event, fromBroswe) {
    let file = event.target.files[0];
    CSVFileValidator(file, CSVConfig)
      .then((csvData) => {
        if (csvData.inValidData.length > 0) {
          this.checkCsvValidation(file, csvData); // 调用校验函数
          this.setState({ csvError: csvData.inValidData });
        } else {
          this.checkCsvValidation(file, csvData); // 调用校验函数
          this.setState({ csvError: [] });
        }
      })
      .catch((err) => {});
  }

CSV校验函数

checkCsvValidation = async (file, csvData) => {
    this.setState({ submitting: true });
    const text = await file.text();
    let result = parse(text, { header: true, skipEmptyLines: true });
    let errorIndexes = [];
    let emails = [];

    csvData.inValidData.map((item) => {
      errorIndexes.push(item.rowIndex - 2);
    });

    if (result.data.length < 2) {
      this.state.csvError.push({ message: "请至少插入2条记录" });
      this.setState({ resetFlag: true });
    } 
      for (let i = 0; i < result.data.length; i++) {
        const checkContact = this.formatPhoneNumber(result.data[i].Contact);
        const checkEmail = this.isValidEmail(result.data[i].Email);
        emails.push(result.data[i].Email);

        if (
          checkContact == false ||
          errorIndexes.includes(i) ||
          checkEmail == false
        ) {
          if (checkContact == false) {
            this.state.csvError.push({
              message: `<strong>第${i + 2}行/第4列</strong>的联系电话无效`,
            });
            this.setState({ resetFlag: true });
          }
          if (checkEmail == false) {
            this.state.csvError.push({
              message: `<strong>第${i + 2}行/第3列</strong>的邮箱无效`,
            });
            this.setState({ resetFlag: true });
          }
          result.data[i].status = false;
          continue;
        }

        result.data[i].status = true;
        result.data[i].Contact = checkContact;
      }
    
    this.setState({ csvData: result.data, submitting: false, showTable: true });
  };
问题分析与修复方案

核心问题点

  1. 直接修改State导致内存堆积:
    代码中直接调用this.state.csvError.push()添加错误信息,没有通过setState更新状态。这会让React无法正确追踪状态变化,旧的错误数据持续堆积在内存中,重复上传后内存占用越来越大。
  2. 重复解析CSV文件:
    CSVFileValidator已经完成文件解析,但checkCsvValidation中又重新调用file.text()和parse解析文件,重复的IO和解析操作会额外占用内存,大文件场景下问题更突出。
  3. 频繁触发不必要的状态更新:
    在循环校验过程中多次调用setState({ resetFlag: true }),每次都会触发组件重渲染,增加内存开销。
  4. 数组查询性能差:
    使用errorIndexes.includes(i)判断行是否有错误,大数组下该方法性能低,且数组本身会持续占用内存。

修复步骤

  1. 通过setState管理错误数组:
    每次校验前创建新的错误数组,避免直接修改原state,最后统一更新状态:
    checkCsvValidation = async (file, csvData) => {
        this.setState({ submitting: true });
        // 复用CSVFileValidator已解析的数据,避免重复解析
        let result = csvData.data;
        let currentErrors = [];
        let errorIndexSet = new Set(csvData.inValidData.map(item => item.rowIndex - 2));
    
        if (result.length < 2) {
            currentErrors.push({ message: "请至少插入2条记录" });
        }
    
        let needReset = false;
        for (let i = 0; i < result.length; i++) {
            const checkContact = this.formatPhoneNumber(result[i].Contact);
            const checkEmail = this.isValidEmail(result[i].Email);
    
            if (checkContact === false || errorIndexSet.has(i) || checkEmail === false) {
                needReset = true;
                if (checkContact === false) {
                    currentErrors.push({
                        message: `<strong>第${i + 2}行/第4列</strong>的联系电话无效`,
                    });
                }
                if (checkEmail === false) {
                    currentErrors.push({
                        message: `<strong>第${i + 2}行/第3列</strong>的邮箱无效`,
                    });
                }
                result[i].status = false;
                continue;
            }
    
            result[i].status = true;
            result[i].Contact = checkContact;
        }
    
        this.setState({
            csvData: result,
            submitting: false,
            showTable: true,
            csvError: currentErrors,
            resetFlag: needReset
        });
    };
    
  2. 复用已解析数据:
    直接使用CSVFileValidator返回的csvData.data,无需重新读取和解析文件,减少内存占用。
  3. 合并状态更新:
    将resetFlag的判断放到循环结束后,统一通过一次setState更新所有状态,避免频繁重渲染。
  4. 优化错误索引查询:
    将errorIndexes转为Set,用has方法判断行是否有错误,提升性能同时减少内存开销。
  5. 清理文件引用:
    在上传处理函数末尾清空文件输入框的value,帮助垃圾回收:
    changeHandlerForCsvUpload(event, fromBroswe) {
        let file = event.target.files[0];
        CSVFileValidator(file, CSVConfig)
            .then((csvData) => {
                // ... 原有逻辑
            })
            .catch((err) => {})
            .finally(() => {
                event.target.value = ''; // 清空文件引用
            });
    }
    

内容的提问来源于stack exchange,提问作者Asif Mujawar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 08:15:29