首页 > 解决方案 > 使用 CsvHelper 将 csv 文件转换为 excel 时减少内存

问题描述

当我们使用具有 100K 行和 14 列的CSVHelper将 csv 文件转换为 excel 时,它需要 1.5GB 的进程内存。我们需要减少内存。这是 CsvHelper 的问题,占用了那么多内存来转换文件,还是我们在这里做错了什么。是否有解决方法来减少内存。

这是我正在使用的示例程序:

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

namespace ConsoleApp2.Conversion
{
    public class CsvHelperExcelWriter
    {
        public void Process(string csvFilePath)
        {
            try
            {
                Stream ms = new MemoryStream();
                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))
                        {
                            AddHeaders(csv, _worksheet);
                            AddValues(csv, _worksheet);
                        }
                    }
                    workbook.SaveAs(ms);
                    ms.Position = 0;
                    using (FileStream fileStream = new FileStream("C:\\Projects\\POC\\SampleFile\\Excel\\100000 Sales Records with CsvHelper.xlsx", FileMode.Append, FileAccess.Write))
                    {
                        ms.CopyTo(fileStream);
                        fileStream.Close();
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        private void AddValues(CsvReader csv, IXLWorksheet _worksheet)
        {
            int rowNumber = 2;
            while (csv.Read())
            {
                int cellNumber = 1;
                for (var i = 0; csv.TryGetField(i, out string value); i++)
                {
                    _worksheet.Cell(rowNumber, cellNumber).SetValue(value);
                    cellNumber++;
                }
                rowNumber++;
            }
        }

        private void AddHeaders(CsvReader csv, IXLWorksheet _worksheet)
        {
            int index = 1;
            _ = csv.ReadHeader();
            foreach (var header in csv.HeaderRecord)
            {
                _worksheet.Cell(1, index).Value = header;
                index++;
            }
        }

    }
}

标签: c#excelcsvcsvhelper

解决方案


通常,高内存使用率是因为在创建 XLSX 文件时所有 Excel 库都使用最终 XML 序列化。为了解决这个问题,我创建了自己的SwiftExcel库,将数据直接输出到文件中,省略了实际的 XML 序列化。

在您的情况下,我建议使用ExcelDataReader之类的高效工具读取 CSV,然后使用SwiftExcel输出。

这是一个例子。首先阅读您的 CSVExcelDataReader并收集所有数据:

var list = new List<List<string>>();
using (var stream = File.Open("C:\\temp\\input.xlsx", FileMode.Open, FileAccess.Read))
{
    using (var reader = ExcelReaderFactory.CreateCsvReader(stream))
    {
        while (reader.Read())
        {
            var subList = new List<string>();
            for (var i = 0; i < reader.FieldCount; i++)
            {
                subList.Add(reader.GetValue(i)?.ToString());
            }
            list.Add(subList);
        }
    }
} 

现在使用以下命令将此数据输出到 Excel 中SwiftExcel

using (var ew = new ExcelWriter("C:\\temp\\test.xlsx"))
{
    for (var row = 1; row <= list.Count; row++)
    {
        var subList = list[row-1];
        for (var col = 1; col <= subList.Count; col++)
        {
            ew.Write(subList[col-1], col, row);
        }
    }
}

仍然存在内存问题,因为您首先收集所有 CSV 数据,但至少它没有使用 XML 序列化那么高。您可以尝试通过将这 2 个循环合并为一个并在读取时同时输出值来消除这种情况。


推荐阅读