C#数据导出无银弹,关键在格式、数据量及样式需求:CSV需转义双引号并加UTF-8 BOM;Excel推荐EPPlus 5.10.4,大数据量应逐单元格写入;统一用DataTable可复用逻辑;大文件须流式分块压缩。

直接说结论:C# 数据导出没有“银弹”,选错方式会导致内存爆掉、Excel 打不开、中文乱码、日期变数字——关键不在“怎么写”,而在“导出什么格式”“数据量多大”“要不要样式和公式”。
导出 CSV:简单但必须处理分隔符和换行
CSV 最轻量,适合纯数据、无格式、百万行以下的导出。问题不在 StreamWriter 本身,而在字段里含逗号、双引号或换行符(比如用户评论)。
- 必须用双引号包裹所有字段,且字段内双引号要转义为两个双引号(
"abc""def") - 不能直接
string.Join(",", row),会崩;推荐用Microsoft.VisualBasic.FileIO.TextFieldParser的反向逻辑,或用现成的StringBuilder手动拼接 - 中文务必指定 UTF-8 BOM:
new StreamWriter(path, false, Encoding.UTF8),否则 Excel 默认用 GBK 打开就乱码
导出 Excel(.xlsx):别碰 EPPlus 6.x 以上版本的非 LTS 分支
EPPlus 是最常用的库,但 6.2+ 版本强制要求商业许可(个人项目也扫 License),且默认启用加密校验,没配好会抛 System.Security.Cryptography.CryptographicException。
- 生产环境建议锁死
EPPlus 5.10.4(MIT 协议,无需许可) - 大数据量(>10 万行)别用
worksheet.Cells["A1"].LoadFromCollection(...),它会一次性加载全部对象进内存;改用worksheet.Cells[row, col].Value = value循环写入 - 设置列宽前先调用
worksheet.Cells.AutoFitColumns(),否则中文列宽可能为 0
导出到 DataTable 再统一处理:避免重复造轮子
如果系统里已有多个导出入口(Web API、WinForm、后台任务),把数据先转成 DataTable,再交给一个通用导出器,能大幅减少编码和测试成本。
-
DataTable天然支持列名、类型、空值,导出时可自动映射为 Excel 表头或 CSV 字段名 - 注意
DateTime列在 Excel 中显示为数字(如44927.5):需调用worksheet.Column(i).Style.Numberformat.Format = "yyyy-mm-dd hh:mm:ss" - 不要依赖
DataTable.WriteXml()或DataSet.GetXml()导出 Excel,它们生成的是旧版 .xls 格式,现代 Excel 打开会报“文件已损坏”
大文件导出(>50MB)必须流式处理 + 前端断点续传配合
后端一次性生成大 Excel 文件,容易触发 IIS 超时、Kestrel 内存溢出,前端还可能收不到完整响应。真正的解法是“不生成完整文件”。
- 用
MemoryStream+ZipArchive分块压缩:每 5 万行一个 sheet,打包成 zip 流式返回 - Web API 返回
FileStreamResult,并设Response.Headers.Add("X-Content-Length", totalSize.ToString()),方便前端校验完整性 - 若需断点续传,后端改用
Range支持的PhysicalFileResult,但前提是文件已落盘且不可被并发修改
真正卡住人的从来不是“怎么调用 SaveAs()”,而是导出后用户说“时间列变成 44927”“导出的 Excel 打不开”“导了 20 分钟还没反应”——这些都得在选型阶段就预判,而不是堆完代码再 debug。











