应使用epplus等真正支持.xlsx格式的库导出,避免用csv/html伪装.xlsx;需处理dbnull、日期格式、中文乱码、列宽适配等问题,优先从datasource导出并设置微软雅黑字体与自动列宽。

导出时Excel打开提示“文件格式与扩展名不匹配”
这是最常见的问题,本质是生成的文件内容是CSV或HTML伪装成.xlsx,而Excel 2007+严格校验OOXML结构。用DataGridView.Rows.Add()拼接CSV字符串再保存为.xlsx后缀,必然触发该警告。
正确做法是使用真正支持.xlsx格式的库,而非文本写入。推荐两个轻量方案:
- 若项目已引用
Microsoft.Office.Interop.Excel:仅限Windows、需本机装Office、有进程残留风险,不推荐新项目 - 首选
EPPlus(v6+需.NET 5+,v5支持.NET Framework 4.5+):纯托管、无依赖、性能好、API清晰
安装:Install-Package EPPlus(注意v6起需调用ExcelPackage.LicenseContext = LicenseContext.NonCommercial;用于非商业用途)
用EPPlus把DataGridView数据写入Excel工作表
核心是跳过UI控件直接读取DataGridView.DataSource或遍历Rows——但必须处理DataGridViewRow.Cells中可能存在的DBNull.Value、空字符串、日期格式错乱等问题。
实操建议:
- 优先从
DataSource导出(如DataTable或BindingList<t></t>),避免UI层渲染逻辑干扰数据源 - 若只能用
Rows,需过滤掉IsNewRow == true的空白行:if (row.IsNewRow) continue; - 单元格值取
cell.Value前先判空:cell.Value == null || cell.Value == DBNull.Value ? "" : cell.Value.ToString() - 日期列用
cell.Value as DateTime?判断类型,再调用worksheet.Cells[rowIndex, colIndex].Style.Numberformat.Format = "yyyy-mm-dd";统一格式
简例(导出到内存流):
本文档主要介绍如何通过python对office excel进行读写操作,使用了xlrd、xlwt和xlutils模块。另外还演示了如何通过Tcl tcom包对excel操作。感兴趣的朋友可以过来看看
using (var package = new ExcelPackage())
{
var worksheet = package.Workbook.Worksheets.Add("Data");
// 写表头
for (int i = 0; i
<h3>导出大表格时卡死或内存溢出</h3>
<p>直接遍历几千行×上百列的<code>DataGridView</code>并逐单元格赋值,会因频繁的COM互操作(Interop)或对象创建导致GC压力剧增。EPPlus虽快,但若在UI线程同步执行,仍会阻塞界面。</p>
<p>关键对策:</p>
- 用
Task.Run()把导出逻辑移出UI线程,配合await防假死 - 禁用
DataGridView.AutoResizeColumns()等耗时渲染行为(导出前设dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;) - 对超大数据源(>10万行),改用
DataTable作为中间载体,用worksheet.Cells.LoadFromDataTable()批量写入,比循环快5–10倍 - 避免在循环内反复访问
dgv.Rows[i].Cells[j].Value——先缓存整行DataGridViewRow再读取
中文乱码、列宽自动适应失效
EPPlus默认字体是Calibri,不支持中文全角字符,且不会自动按内容撑开列宽。这不是bug,是设计使然。
必须手动干预:
- 设置全局字体:
package.Workbook.Properties.Title = "Export"; package.Workbook.DefaultStyle.Font.Name = "微软雅黑"; - 表头加粗并自动列宽:
worksheet.Cells["1:1"].Style.Font.Bold = true; worksheet.Cells.AutoFitColumns();(注意:此方法对含换行符或长文本效果有限) - 更稳的列宽控制:循环列,按最大字符数估算宽度(中文按2字符/英文1字符计),再设
worksheet.Column(i).Width = width; - 若导出含公式或特殊格式的单元格,
Cells[i,j].Value只取结果值,原始格式会丢失——此时应放弃DataGridView直导,改从原始数据源重建
真正麻烦的从来不是“怎么导出来”,而是“导出来的Excel能不能被业务人员双击就打开、不报错、不丢格式、不乱码”。这些细节藏在AutoFitColumns()没生效的瞬间,藏在DBNull没判空的第37行,也藏在你忘记给LicenseContext赋值导致生产环境抛异常的深夜。










