项目地址: https://github.com/nissl-lab/npoi
NuGet:
NPOI最新版本: 2.8.1(2026-10-07 仓库最后提交;截至 2026-10,NuGet 实测)
官方文档: https://github.com/nissl-lab/npoi/wiki
许可证: Apache-2.0
注意(OSMF): 自 2.8.0 起 NuGet 包附带 OSMF EULA,年收入超过 1 万美元的组织/商业用户需通过 GitHub Sponsors 支付月度维护费;初创、自由职业者与业余项目免费。详见 https://github.com/nissl-lab/npoi 与 https://opensourcemaintenancefee.org/consumers/。
概述
NPOI 主要能力:
- xls(HSSF) / xlsx(XSSF) / 流式 xlsx(SXSSF) 读写
- 公式计算:
FormulaEvaluator - 样式:字体、边框、对齐、数据格式、条件格式
- 图表 / 图片 / 批注
- Word
.doc(HWPF) / Word.docx(XWPF) - PowerPoint
.ppt/.pptx - 跨平台:.NET Framework / .NET 6+ / Linux / macOS
大文件写入推荐 SXSSF(流式),可处理百万行 Excel;大文件读取推荐 EventModel(XSSF SAX)。
安装
方式一:dotnet CLI
bashdotnet add package NPOI
方式二:PackageReference
xml<ItemGroup> <PackageReference Include="NPOI" Version="2.8.1" /> </ItemGroup>
方式三:包管理器控制台
powershellInstall-Package NPOI -Version 2.8.1
验证安装
bashdotnet list package | grep -i npoi
主要命名空间:
NPOI.HSSF.UserModel— xlsNPOI.XSSF.UserModel— xlsxNPOI.XSSF.Streaming— 流式 xlsxNPOI.SS.UserModel— 通用接口(IWorkbook / ISheet / IRow / ICell)NPOI.SS.Util— CellRangeAddress、WorkbookUtil
核心 API 速查
| 对象 | 入口 | 说明 |
|---|---|---|
| 工作簿 | new XSSFWorkbook() / new HSSFWorkbook() / new SXSSFWorkbook(rowAccessWindow) | xlsx / xls / 流式写大文件 |
| 读任意格式 | WorkbookFactory.Create(stream) | 按魔数自动识别 xls/xlsx |
| 表 | wb.CreateSheet(name) / wb.GetSheetAt(i) / wb.GetSheet(name) | |
| 行 / 单元格 | sh.CreateRow(i) / row.CreateCell(j) | |
| 单元格值 | cell.SetCellValue(...) / cell.CellType | 枚举 CellType.String/Numeric/Boolean/Formula/Blank |
| 样式 | wb.CreateCellStyle() + wb.CreateFont() + wb.CreateDataFormat() | |
| 公式求值 | wb.GetCreationHelper().CreateFormulaEvaluator() | 读公式结果用它,不要自己解析 |
| Word | XWPFDocument / XWPFParagraph | .docx 读写 |
写入 Excel(xlsx)
csharpusing NPOI.SS.UserModel; using NPOI.XSSF.UserModel; IWorkbook wb = new XSSFWorkbook(); ISheet sh = wb.CreateSheet("Sheet1"); // 标题样式 ICellStyle headStyle = wb.CreateCellStyle(); IFont font = wb.CreateFont(); font.Boldweight = (short)FontBoldWeight.Bold; font.FontHeightInPoints = 12; headStyle.SetFont(font); headStyle.FillForegroundColor = IndexedColors.Grey25Percent.Index; headStyle.FillPattern = FillPattern.SolidForeground; headStyle.Alignment = HorizontalAlignment.Center; string[] head = { "ID", "Name", "Score", "Time" }; IRow row = sh.CreateRow(0); for (int i = 0; i < head.Length; i++) { var c = row.CreateCell(i); c.SetCellValue(head[i]); c.CellStyle = headStyle; } // 数据 for (int r = 1; r <= 10; r++) { var dr = sh.CreateRow(r); dr.CreateCell(0).SetCellValue(r); dr.CreateCell(1).SetCellValue("User" + r); dr.CreateCell(2).SetCellValue(80 + r); dr.CreateCell(3).SetCellValue(DateTime.Now); // 注意要设置日期样式 } // 列宽:AutoSizeColumn 依赖字体度量,Linux/容器里常失败,生产代码建议显式设宽 // (1/256 个字符宽,20 字符宽 ≈ 20 * 256) for (int i = 0; i < head.Length; i++) sh.SetColumnWidth(i, 20 * 256); using (var fs = new FileStream("out.xlsx", FileMode.Create)) { wb.Write(fs, leaveOpen: false); }
读取 Excel
csharpusing var fs = new FileStream("in.xlsx", FileMode.Open, FileAccess.Read); IWorkbook wb = WorkbookFactory.Create(fs); // 自动识别 xls/xlsx ISheet sh = wb.GetSheetAt(0); for (int r = sh.FirstRowNum; r <= sh.LastRowNum; r++) { IRow row = sh.GetRow(r); if (row == null) continue; for (int c = row.FirstCellNum; c < row.LastCellNum; c++) { ICell cell = row.GetCell(c); Console.Write(GetValue(cell) + "\t"); } Console.WriteLine(); } static object GetValue(ICell? cell) => cell?.CellType switch { CellType.Numeric => DateUtil.IsCellDateFormatted(cell) ? cell.DateCellValue : cell.NumericCellValue, CellType.String => cell.StringCellValue, CellType.Boolean => cell.BooleanCellValue, CellType.Formula => cell.CachedFormulaResultType switch { CellType.Numeric => cell.NumericCellValue, CellType.String => cell.StringCellValue, _ => "" }, _ => "" };
数据格式(日期 / 数字)
csharpICellStyle dateStyle = wb.CreateCellStyle(); dateStyle.DataFormat = wb.CreateDataFormat().GetFormat("yyyy-MM-dd HH:mm:ss"); ICellStyle pctStyle = wb.CreateCellStyle(); pctStyle.DataFormat = HSSFDataFormat.GetBuiltinFormat("0.00%");
公式
csharpsh.CreateRow(11).CreateCell(2).SetCellFormula("SUM(C2:C11)"); // 计算公式(写出前) var ev = wb.GetCreationHelper().CreateFormulaEvaluator(); ev.EvaluateAll();
流式写入(SXSSF,海量行)
csharpusing NPOI.XSSF.Streaming; using SXSSFWorkbook wb = new SXSSFWorkbook(rowAccessWindowSize: 1000); ISheet sh = wb.CreateSheet("big"); for (int r = 0; r < 1_000_000; r++) { var row = sh.CreateRow(r); for (int c = 0; c < 10; c++) row.CreateCell(c).SetCellValue(r * 10 + c); } using (var fs = File.Create("big.xlsx")) { wb.Write(fs); } wb.Dispose(); // SXSSF 必须 Dispose 才会清理磁盘临时文件
合并单元格 / 冻结 / 筛选
csharpsh.AddMergedRegion(new CellRangeAddress(0, 0, 0, 3)); // 合并第一行 A:D sh.CreateFreezePane(0, 1); // 冻结首行 sh.SetAutoFilter(new CellRangeAddress(0, sh.LastRowNum, 0, 3));
图片
csharpbyte[] img = File.ReadAllBytes("logo.png"); int picIdx = wb.AddPicture(img, PictureType.PNG); IDrawing patriarch = sh.CreateDrawingPatriarch(); var anchor = wb.GetCreationHelper().CreateClientAnchor(); anchor.Col1 = 5; anchor.Row1 = 1; var pic = patriarch.CreatePicture(anchor, picIdx); pic.Resize();
条件格式
csharpvar cf = (XSSFConditionalFormatting)sh.SheetConditionalFormatting; var rule = cf.CreateConditionalFormattingRule(ComparisonOperator.GreaterThan, "60"); var pattern = rule.CreatePatternFormatting(); pattern.FillBackgroundColor = IndexedColors.Yellow.Index; cf.AddConditionalFormatting(new[] { CellRangeAddress.ValueOf("C2:C11") }, rule);
Word(XWPF)
csharpusing NPOI.XWPF.UserModel; XWPFDocument doc = new(); XWPFParagraph p = doc.CreateParagraph(); XWPFRun r = p.CreateRun(); r.FontSize = 16; r.IsBold = true; r.SetText("Hello NPOI"); using var fs = File.Create("out.docx"); doc.Write(fs);
性能与最佳实践
- 百万级写用 SXSSF,注意
Dispose()清理 tmp 文件 - 样式数量受 64000 限制:复用
ICellStyle实例 - 日期单元格 必须设置 DataFormat,否则显示数字
- 大文件读用
XSSFEventModel(SAX)以减少内存 - xls 行数上限 65536,xlsx 上限 1048576
- 避免在循环内
wb.CreateCellStyle()
典型工作流
工作流一:导出报表(写 xlsx)
new XSSFWorkbook()→CreateSheet(),先铺数据:CreateRow()/CreateCell()/SetCellValue()- 再设样式:循环外建好
ICellStyle/IFont/IDataFormat实例复用,日期列套带日期格式的样式 - 有公式要提前
CreateFormulaEvaluator().EvaluateAll()把结果算出来 FileStream写出后关闭;服务端环境无需装 Office(纯托管)
工作流二:导入解析(读 xls/xlsx)
FileStream→WorkbookFactory.Create(fs)自动识别格式GetSheetAt(0)(或按名字取表)→ 遍历Row/Cell- 按
CellType分支取值:String/Numeric/Boolean/Formula(公式用IFormulaEvaluator求值) - 数据入库前做格式校验,行数接近上限时(xls 65536 / xlsx 1048576)考虑拆分
工作流三:百万行导出
- 写用
new SXSSFWorkbook(rowAccessWindowSize: 1000)流式滚动,只保留窗口内的行在内存 - 用完后
Dispose()清理临时文件 - 读大文件改用
XSSFEventModel(SAX)遍历
常见问题
| 问题 | 解决 |
|---|---|
| 中文乱码 | 文本本身是 UTF-8,.xlsx 不会乱码;.xls(HSSF)在 Excel 里按本地代码页显示,建议中文场景统一用 xlsx |
| 日期变数字(显示 45000 之类) | 单元格要套带日期 DataFormat 的 ICellStyle,只 SetCellValue(DateTime) 不够 |
| Office 报「此工作簿包含无法读取的内容」 | 多为样式数超 64000 或公式非法:把 CreateCellStyle() 提到循环外复用,并检查 SetCellFormula 的表达式 |
大文件 OutOfMemoryException | 写用 SXSSFWorkbook(记得 Dispose() 清临时文件),读用 XSSF EventModel(SAX) |
| 打开提示文件损坏 | wb.Write(fs) 要写在 FileStream 作用域内,且不要把 leaveOpen 误设为 true 后又提前 dispose 流 |
AutoSizeColumn 抛异常或列宽为 0 | xlsx 下自动列宽依赖字体度量,Linux/容器里常失败;改成显式 SetColumnWidth(col, width*256) |
AI 使用建议
推荐工作流
- 确定格式:
.xls用 HSSFWorkbook,.xlsx用 XSSFWorkbook,不确定用WorkbookFactory.Create()自动识别 - 读文件:
FileStream→WorkbookFactory.Create()→GetSheetAt(0)→ 遍历 Row/Cell → 根据CellType取值 - 写文件:
new XSSFWorkbook()→CreateSheet()→CreateRow()→CreateCell()→SetCellValue()→FileStream写出 - 样式后设:先写数据,再设样式(复用
ICellStyle实例,避免 64000 限制) - 大文件处理:写用 SXSSFWorkbook(流式),读用 EventModel(SAX 事件驱动)
关键模式与常见陷阱
- 日期变数字:日期单元格必须设置带日期 DataFormat 的
ICellStyle,否则显示为数字 - 样式 64000 限制:
ICellStyle实例总数不能超过 64000,必须在循环外CreateCellStyle()并复用 - 中文不乱码的前提:xlsx 默认 UTF-8 不会乱码;xls 需要检查 Excel 打开时自动识别
- 公式需要求值:
wb.GetCreationHelper().CreateFormulaEvaluator().EvaluateAll()在写出前调用 - SXSSF 记得 Dispose:流式写入后必须
Dispose()清理临时文件 - 不要用 COM/Interop:NPOI 纯托管,不依赖 Office 安装,服务器环境友好
如何选择正确方案
| 场景 | 推荐方案 |
|---|---|
| 简单导入导出(< 10 万行) | NPOI XSSFWorkbook |
| 百万级写入 | NPOI SXSSFWorkbook |
| 百万级读取 | NPOI EventModel(SAX) |
| WinForms/WPF 内嵌表格 | ReoGrid(底层用 NPOI 读写文件) |
| 纯数据交换(无格式) | MiniExcel / CsvHelper |
相关技能
- reogrid — .NET 电子表格控件,内嵌可编辑表格,底层依赖 NPOI 读写 Excel 文件:../reogrid/SKILL.md
- sqlsugar — .NET ORM,可与 NPOI 配合:从数据库查询后批量导出 Excel:../sqlsugar/SKILL.md
- sod — 另一套 .NET ORM,导出场景同样先查库再交给 NPOI 写表:../sod/SKILL.md
参考资源
- Wiki:https://github.com/nissl-lab/npoi/wiki
- Apache POI(API 等价):https://poi.apache.org/components/spreadsheet/
- 中文教程(znlgis):https://znlgis.github.io/csharp/npoi/


