C#使用NPOI创建/追加.xlsx文件报错、文件损坏问题咨询
C# NPOI操作.xlsx文件异常解决方案
问题现象
- 基于NPOI开发.xlsx文件写入逻辑时,初期测试抛出
MissingMethod异常,尝试删除packages文件夹、重载全部NuGet包等常规方案后问题未解决 - 代码运行后目标路径会生成指定文件,但文件大小为0字节;偶发无显式报错的场景下,生成的文件也处于损坏状态,Excel打开时提示文件错误,仅能通过修复功能恢复部分数据
- 当前运行抛出明确异常,核心日志提示无法加载
ICSharpCode.SharpZipLib程序集
涉及的业务代码如下:
public void WriteExcel() { System.IO.Directory.CreateDirectory(workingDirectory + @"\Excel"); var newFile = workingDirectory + @"\Excel\data.xlsx"; try { if (Directory.Exists(workingDirectory + @"\Excel")) { if (!File.Exists(newFile)) { using (var fs = new FileStream(newFile, FileMode.Create, FileAccess.Write)) { IWorkbook workbook = new XSSFWorkbook(); ISheet sheet = workbook.CreateSheet("Data"); IRow row = sheet.CreateRow(0); ICell cell1 = row.CreateCell(0); ICell cell2 = row.CreateCell(1); ICell cell3 = row.CreateCell(2); cell1.SetCellValue("Date"); cell2.SetCellValue("Value1"); cell3.SetCellValue("Value2"); for (int i = 0; i <= 20; i++) sheet.AutoSizeColumn(i); workbook.Write(fs); } Console.WriteLine("File is created"); } else { IWorkbook workbook; using (var outs = new FileStream(newFile, FileMode.Open, FileAccess.Read)) { workbook = new XSSFWorkbook(outs); outs.Close(); Console.WriteLine("Workbook read"); } ISheet sheet = workbook.GetSheetAt(0); IRow row = sheet.CreateRow(sheet.LastRowNum+1); ICell cell1 = row.CreateCell(0); ICell cell2 = row.CreateCell(1); ICell cell3 = row.CreateCell(2); cell1.SetCellValue(DateTime.Today.Date.ToString("yyyy-MM-dd")); cell2.SetCellValue("Some value"); cell3.SetCellValue("some value 2"); Console.WriteLine("Values set"); using (var ins = new FileStream(newFile, FileMode.Open, FileAccess.Write)) { workbook.Write(ins); ins.Close(); Console.WriteLine("Workbook saved"); } } } else { // 目录创建失败逻辑预留 } } catch (Exception e) { Console.WriteLine(e); } }
异常日志原文:
Exception thrown: 'System.IO.FileNotFoundException' in NPOI.OpenXml4Net.dll Exception thrown: 'NPOI.POIXMLException' in NPOI.OOXML.dll System.IO.FileNotFoundException: Det går inte att läsa in filen eller sammansättningen ICSharpCode.SharpZipLib
日志中瑞典语段含义为:无法加载文件或程序集ICSharpCode.SharpZipLib
根因分析
- 直接触发报错的原因:NPOI处理.xlsx(OOXML格式)文件时,强依赖
ICSharpCode.SharpZipLib完成OpenXML包的压缩、解压操作。当前项目中该依赖要么未安装,要么安装的版本和NPOI版本不匹配,或生成时未将该dll拷贝到输出目录,直接触发FileNotFoundException,后续连锁抛出POIXMLException,导致文件写入中断出现0字节、损坏问题。最开始出现的MissingMethod异常,本质也是依赖版本冲突导致的方法签名不匹配。 - 代码本身存在两个会导致文件损坏的逻辑缺陷:
- 追加写入已有Excel文件时,保存文件使用
FileMode.Open模式打开写流,该模式不会清空原有文件内容,新写入的字节和旧文件内容叠加会直接破坏xlsx的压缩包结构 - 新建文件时遍历0-20列做自动列宽,但实际仅初始化了前3列的单元格,部分NPOI版本处理未创建单元格的空列自动列宽逻辑时会抛出空引用异常
- using块会在代码执行离开块作用域时自动释放流资源,手动调用
Close()存在流提前释放、写入内容不完整的风险
- 追加写入已有Excel文件时,保存文件使用
修复步骤
- 修复依赖问题
- 打开NuGet包管理器,搜索安装
SharpZipLib包,注意版本匹配:NPOI 2.5.x及以上版本对应SharpZipLib 1.3.0及以上版本;NPOI 2.4及以下老版本对应SharpZipLib 0.86版本。 - 安装完成后执行项目清理,再重新生成,确认项目输出目录下存在
ICSharpCode.SharpZipLib.dll文件。
- 打开NuGet包管理器,搜索安装
- 修正代码逻辑
- 调整自动列宽的循环范围,仅对实际创建了单元格的列执行自动调整
- 保存已有工作簿时,将写文件流的打开模式改为
FileMode.Create,写入前清空旧文件内容避免结构损坏 - 去掉using块内冗余的手动流Close调用
核心修正点参考:
// 新建文件时的自动列宽逻辑修正 for (int i = 0; i < 3; i++) sheet.AutoSizeColumn(i); // 读取已有工作簿的逻辑去掉冗余Close using (var outs = new FileStream(newFile, FileMode.Open, FileAccess.Read)) { workbook = new XSSFWorkbook(outs); Console.WriteLine("Workbook read"); } // 保存工作簿的流模式修正 using (var ins = new FileStream(newFile, FileMode.Create, FileAccess.Write)) { workbook.Write(ins); Console.WriteLine("Workbook saved"); }
- 验证:重新运行程序,确认生成的xlsx文件大小不为0,可直接通过Excel正常打开,无修复提示即为修复完成。
内容的提问来源于stack exchange,提问作者lemonslayer
相关产品推荐
相关产品推荐

