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

使用CsvHelper转换CSV为Excel时内存占用过高的优化咨询

问题原因定位

首先可以明确:高内存占用不是CsvHelper导致的,CsvHelper默认是流式读取CSV,不会一次性加载全量文件到内存,问题出在两个方面:

  • ClosedXML本身是基于内存的Office操作库,会将整个工作簿的所有单元格、元数据全部加载到内存中维护,10万行14列的数据集加上单元格的附加属性,内存占用本身就会很高
  • 你的代码中额外引入了MemoryStream存储完整的生成后Excel文件,相当于全量Excel数据在内存中冗余存储了一份,进一步抬高了内存峰值
可行优化方案

方案1:基于现有ClosedXML依赖最小改动优化(内存预计降到500MB~800MB)

  1. 移除冗余的MemoryStream,直接将Workbook保存到文件流,避免全量Excel数据在内存中重复缓存
  2. 替换逐单元格赋值逻辑,使用ClosedXML内置的InsertData批量插入数据,减少单元格对象的重复创建开销
  3. 开启ClosedXML的性能优化配置

优化后的代码示例:

using ClosedXML.Excel;
using CsvHelper;
using System.Globalization;
using System.IO;
using System.Collections.Generic;

namespace ConsoleApp2.Conversion
{
    public class CsvHelperExcelWriter
    {
        public void Process(string csvFilePath)
        {
            // 配置ClosedXML全局性能选项,减少默认样式开销
            XLWorkbook.DefaultStyle.Font.FontSize = 11;
            XLWorkbook.DefaultStyle.Font.FontName = "Calibri";

            using var workbook = new XLWorkbook(XLEventTracking.Disabled);
            var worksheet = workbook.AddWorksheet("Sheet1");
            
            using var csv = new CsvReader(new StreamReader(csvFilePath), CultureInfo.InvariantCulture);
            csv.Read();
            if (string.IsNullOrWhiteSpace(csv.Context.Parser.RawRecord)) return;
            
            // 写入表头
            csv.ReadHeader();
            worksheet.Cell(1, 1).InsertData(new[] { csv.HeaderRecord }, transpose: false);
            
            // 批量写入数据,每次读1000行插入一次,可根据需要调整批次大小
            var batch = new List<string[]>(1000);
            int rowOffset = 2;
            while (csv.Read())
            {
                batch.Add(csv.Parser.Record);
                if (batch.Count >= 1000)
                {
                    worksheet.Cell(rowOffset, 1).InsertData(batch, transpose: false);
                    rowOffset += batch.Count;
                    batch.Clear();
                }
            }
            // 插入剩余不满1000行的批次
            if (batch.Count > 0)
            {
                worksheet.Cell(rowOffset, 1).InsertData(batch, transpose: false);
            }
            
            // 直接保存到文件,无需经过MemoryStream中转
            workbook.SaveAs(@"C:\Projects\POC\SampleFile\Excel\100000 Sales Records with CsvHelper.xlsx");
        }
    }
}

方案2:替换为流式Excel写入库(内存预计降到100MB以下)

如果需要更低的内存占用,建议放弃ClosedXML,改用支持流式写入的Excel库,比如NPOI的SXSSF实现,或者EPPlus的流式写入接口,这类库会将超出缓存数量的行临时写入磁盘临时文件,内存中仅保留最近几千行数据,10万行级别的转换内存峰值通常可以控制在几十MB。

示例使用NPOI SXSSF的核心代码逻辑:

using NPOI.XSSF.Streaming;
using CsvHelper;
using System.Globalization;
using System.IO;

// 核心处理逻辑
public void Process(string csvFilePath)
{
    // 仅保留最近1000行在内存,其余写入临时文件
    var workbook = new SXSSFWorkbook(1000);
    var sheet = workbook.CreateSheet("Sheet1");
    
    using var csv = new CsvReader(new StreamReader(csvFilePath), CultureInfo.InvariantCulture);
    csv.Read();
    if (string.IsNullOrWhiteSpace(csv.Context.Parser.RawRecord)) return;
    
    // 写表头
    csv.ReadHeader();
    var headerRow = sheet.CreateRow(0);
    for (int i = 0; i < csv.HeaderRecord.Length; i++)
    {
        headerRow.CreateCell(i).SetCellValue(csv.HeaderRecord[i]);
    }
    
    // 逐行写入,内存仅保留最近1000行
    int rowIndex = 1;
    while (csv.Read())
    {
        var row = sheet.CreateRow(rowIndex);
        for (int i = 0; i < csv.Parser.Record.Length; i++)
        {
            row.CreateCell(i).SetCellValue(csv.GetField(i));
        }
        rowIndex++;
    }
    
    // 保存到文件
    using var fs = new FileStream(@"C:\Projects\POC\SampleFile\Excel\100000 Sales Records with CsvHelper.xlsx", FileMode.Create, FileAccess.Write);
    workbook.Write(fs);
    // 清理生成的临时文件
    workbook.Dispose();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 17:09:02