正确做法是用sqldatareader流式读取配合streamwriter逐行写csv,避免内存溢出;导出excel优先用utf-8+bom的csv伪装.xlsx后缀,而非硬生成xlsx或使用com组件。

用 SqlDataReader 流式写入 CSV,避免内存爆炸
直接把几百万行查出来再拼字符串,OutOfMemoryException 很快就来。正确做法是边读边写,用 StreamWriter 配合 SqlDataReader 逐行输出:
- 打开
SqlConnection后立即执行SqlCommand.ExecuteReader(CommandBehavior.SequentialAccess),启用流式读取 - 用
StreamWriter的WriteLine写表头,注意字段名含逗号、换行、双引号时要按 RFC 4180 规则转义(用双引号包裹,内部双引号变成两个) - 循环
reader.Read(),对每列调用reader.GetValue(i),判空后转字符串并转义 - 别用
DataTable.Load(reader)—— 它会把全部数据缓存进内存
导出 Excel 不要用第三方库硬生成 .xlsx?试试 CSV + 后缀伪装
纯 C# 无依赖导出真 Excel(.xlsx)极其复杂,涉及 Open XML SDK、ZIP 封装、关系定义等。多数业务场景下,用户只要“能双击用 Excel 打开”,这时更稳的方案是:导出标准 CSV,但文件名强制用 .xlsx 后缀,并设置 MIME 类型为 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet。
- Excel 2007+ 对 UTF-8 编码的 CSV 自动识别良好,但需在文件开头加 BOM(
new UTF8Encoding(true)) - 如果必须用真实 .xlsx,推荐
EPPlus(注意 v6+ 需商业授权),避免用已停止维护的NPOI处理新格式 - 千万别用 COM 组件(
Microsoft.Office.Interop.Excel)—— 服务器端不可靠,权限和进程残留问题多
SqlDataAdapter.Fill(DataTable) 适合小数据,但要注意字符集和 NULL 处理
当结果确定小于 10 万行且需做简单加工(如列重命名、空值替换)时,用 DataTable 是可接受的折中方案,但几个坑必须避开:
-
DataTable默认把 SQL Server 的datetime2当作DateTime,但导出 CSV 时若含毫秒精度,Excel 可能误解析,建议统一转成字符串格式yyyy-MM-dd HH:mm:ss.fff - 数据库字段为
NULL时,DataRow[i]是DBNull.Value,不是null,直接.ToString()会得 "NULL" 字符串,应先判断IsDBNull - 导出前调用
dataTable.CaseSensitive = false和dataTable.Locale = CultureInfo.InvariantCulture,避免排序或比较引发意外
Web 场景下导出大文件必须设超时、禁用缓冲、及时释放连接
ASP.NET Core 或传统 WebForms 中触发导出,常见错误是连接未关闭、响应缓冲未禁用,导致 IIS/Kestrel 超时或内存积压:
- 在
HttpResponse上调用Response.BufferOutput = false(.NET Framework)或HttpContext.Response.BodyWriter直接写(.NET 6+) - 显式设置
CommandTimeout(如 300 秒),并用try/finally确保connection.Close()执行 - 不要在导出逻辑里调用
Response.Redirect或修改Response.StatusCode,否则可能中断流式输出
导出看似简单,真正卡住人的从来不是语法,而是大数据量下的内存控制、编码一致性、以及 Excel 对 CSV 的隐式解析规则——这些细节不提前验证,上线后用户一下载就报错。










