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

C#使用XLWorkbook下载大数据遇内存问题,求MemoryStream替代方案

大数据导出Excel/CSV的内存优化方案

针对你处理100M数据时内存占满的问题,核心优化方向是避免全量数据加载到内存,直接流式输出到响应流,同时根据你实际需要的导出格式(CSV/XLSX)选择对应方案:

方案1:直接流式导出CSV(匹配你当前代码的输出意图)

你的代码中实际设置了CSV的响应头,但却用XLWorkbook加载全量数据,这是内存暴涨的主要原因。直接逐行写入Response.OutputStream即可,完全不需要MemoryStream和XLWorkbook:

Response.Clear();
Response.Buffer = false; // 关闭缓冲,避免内存堆积
Response.Charset = "UTF-8";
Response.ContentType = "text/csv";
Response.AddHeader("content-disposition", $"attachment;filename={DateTime.Now:yyyyMMddHHmmss}.csv");

// 写入CSV表头(处理引号转义)
var headers = Global.Data.Columns.Cast<DataColumn>()
    .Select(col => $"\"{col.ColumnName.Replace("\"", "\"\"")}\"");
Response.Write(string.Join(",", headers) + Environment.NewLine);
Response.Flush();

// 逐行写入数据(处理引号转义)
foreach (DataRow row in Global.Data.Rows)
{
    var fields = row.ItemArray.Select(field => 
    {
        var value = field?.ToString() ?? string.Empty;
        return $"\"{value.Replace("\"", "\"\"")}\"";
    });
    Response.Write(string.Join(",", fields) + Environment.NewLine);
    Response.Flush(); // 及时输出到客户端,释放内存
}

Response.End();

方案2:流式导出XLSX(如果需要真正的Excel格式)

如果确实需要导出xlsx文件,不要用XLWorkbook一次性加载全量数据,改用支持流式写入的库,直接输出到Response.OutputStream,无需MemoryStream中转:

用EPPlus实现流式XLSX导出(EPPlus 5+版本支持)

Response.Clear();
Response.Buffer = false;
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", $"attachment;filename={DateTime.Now:yyyyMMddHHmmss}.xlsx");

using (var package = new ExcelPackage(Response.OutputStream))
{
    var worksheet = package.Workbook.Worksheets.Add("Sheet1");
    
    // 写入表头
    int colIndex = 1;
    foreach (DataColumn col in Global.Data.Columns)
    {
        worksheet.Cells[1, colIndex++].Value = col.ColumnName;
    }
    
    // 分批写入数据,控制内存占用
    int rowIndex = 2;
    int batchSize = 1000; // 每批处理1000行
    for (int i = 0; i < Global.Data.Rows.Count; i += batchSize)
    {
        var batchRows = Global.Data.Rows.Cast<DataRow>().Skip(i).Take(batchSize);
        foreach (DataRow row in batchRows)
        {
            colIndex = 1;
            foreach (var item in row.ItemArray)
            {
                worksheet.Cells[rowIndex, colIndex++].Value = item;
            }
            rowIndex++;
        }
        package.Save(); // 及时刷新到响应流
    }
}

Response.End();

用NPOI的SXSSFWorkbook实现流式XLSX导出

SXSSFWorkbook会自动将超出内存限制的行写入临时文件,避免内存溢出:

Response.Clear();
Response.Buffer = false;
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("content-disposition", $"attachment;filename={DateTime.Now:yyyyMMddHHmmss}.xlsx");

// 设置内存中仅保留1000行,超出部分写入临时文件
using (var wb = new SXSSFWorkbook(1000))
{
    var sheet = wb.CreateSheet("Sheet1");
    
    // 写入表头
    var headerRow = sheet.CreateRow(0);
    int colIndex = 0;
    foreach (DataColumn col in Global.Data.Columns)
    {
        headerRow.CreateCell(colIndex++).SetCellValue(col.ColumnName);
    }
    
    // 逐行写入数据
    int rowIndex = 1;
    foreach (DataRow row in Global.Data.Rows)
    {
        var dataRow = sheet.CreateRow(rowIndex++);
        colIndex = 0;
        foreach (var item in row.ItemArray)
        {
            dataRow.CreateCell(colIndex++).SetCellValue(item?.ToString() ?? string.Empty);
        }
    }
    
    wb.Write(Response.OutputStream);
}

Response.End();

你当前代码的核心问题

  1. 用XLWorkbook处理CSV属于冗余操作,完全没必要加载全量数据到内存
  2. Response.Buffer = true会将整个输出内容缓冲到内存,大数据场景下必然导致内存暴涨
  3. 使用MemoryStream中转完全多余,直接写入Response.OutputStream即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 22:57:29