NPOI (.NET version of POI) 是一个开源的 .NET 库,用于读写 Microsoft Office 格式文件,特别是 Excel。它移植自 Java 的 POI 项目,是 .NET 平台下功能最全面的免费 Office 文档操作库。
核心优势:
访问架构:
IWorkbook (工作簿) // 管理整个 Excel 文件 ↓ISheet (工作表) // 管理单个工作表 ↓IRow (行) // 管理单行数据 ↓ICell (单元格) // 操作单个单元格1.新建C#类库,命名为“ExcelToolLibCSharp”

类库项目
2.添加NPOI库

3.添加一个ExcelOperateObject类

添加状态封装对象:用于封装和管理单个 Excel 文件在整个操作生命周期中的所有状态和资源。它在 NPOI 操作库中扮演着数据中心和资源管理器的角色。
using NPOI.SS.UserModel;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ExcelToolLibCSharp{public class ExcelOperateObject{///<summary>/// 文件流对象///</summary>public FileStream Fs { get; set; }///<summary>/// NPOI 工作簿对象///</summary>public IWorkbook WorkBook { get; set; }///<summary>/// 文件存在状态标志///</summary>public bool FileExists { get; set; }///<summary>/// Excel 文件完整路径///</summary>public string FilePath { get; set; }publicvoidDispose(){WorkBook?.Close();Fs?.Close();Fs?.Dispose();}}}
4.添加一个ExcelHelper类
(1)实例化excel状态对象字典,使用 Handle 机制管理多个 Excel 文件,后续根据key查找对应的excel操作对象
using NPOI.HSSF.UserModel;using NPOI.SS.UserModel;using NPOI.XSSF.UserModel;using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ExcelToolLibCSharp{public classExcelHelper{private Dictionary<int, ExcelOperateObject> excelOperateDictionary;public ExcelHelper(){excelOperateDictionary = new Dictionary<int, ExcelOperateObject>();}}}
(2)每个 Excel 文件分配唯一句柄
///<summary>/// 自动句柄分配算法///</summary>///<param name="keys"></param>///<returns></returns>privateintFindMinMissingKey(List<int> keys){keys.Sort(); // 排序现有键值int minMissing = 1;foreach (int key in keys){if (key > minMissing) return minMissing;minMissing = key + 1;}return minMissing; // 返回最小的可用整数}
(3)实例化excel操作对象,.xlsx对应XSSFWorkbook,.xls对应XSSFWorkbookHSSFWorkbook
///<summary>/// 根据文件路径自动判断excel文件格式,如果存在则打开,否则则新建excel文档///</summary>///<param name="filePath"></param>///<returns>返回句柄</returns>public ExecuteResult<int> CreateOrOpenFile(string filePath){ExcelOperateObject excelOperateObject = new ExcelOperateObject();List<int> keys = excelOperateDictionary.Keys.ToList();int handle = FindMinMissingKey(keys);try{if (!File.Exists(filePath)){excelOperateObject.Fs = new FileStream(filePath, FileMode.CreateNew, FileAccess.ReadWrite);if (filePath.IndexOf(".xlsx") > 0){excelOperateObject.WorkBook = new XSSFWorkbook() as IWorkbook;}else if (filePath.IndexOf(".xls") > 0){excelOperateObject.WorkBook = new HSSFWorkbook() as IWorkbook;}excelOperateObject.FileExists = false;excelOperateObject.FilePath = filePath;}else{var fs = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite);if (filePath.IndexOf(".xlsx") > 0){excelOperateObject.WorkBook = new XSSFWorkbook(fs) as IWorkbook;}else if (filePath.IndexOf(".xls") > 0){excelOperateObject.WorkBook = new HSSFWorkbook(fs) as IWorkbook;}excelOperateObject.FileExists = true;excelOperateObject.FilePath = filePath;excelOperateObject.Fs = fs;}excelOperateDictionary.Add(handle, excelOperateObject);return ExecuteResult<int>.Success(handle);}catch (Exception ex){string logPath = "excelToolLog.txt";using (StreamWriter writer = new StreamWriter(logPath)){writer.WriteLine($"ex.Message:{ex.Message}");}return ExecuteResult<int>.Error($"ex.Message:{ex.Message}");}}
(4)根据句柄,查找Excel操作对象,设置单元格值
///<summary>/// 单元格值设置///</summary>///<param name="sheetName">表格名称</param>///<param name="rowIndex">行数</param>///<param name="columnIndex">列数</param>///<param name="value">设置值</param>///<returns></returns>public ExecuteResult SetCellValue(int handle, string sheetName, int rowIndex, int columnIndex, stringvalue){try{ISheet sheet;IRow row;ICell cell;if (!excelOperateDictionary.ContainsKey(handle)) return ExecuteResult.Error($"handle:{handle}不存在!");ExcelOperateObject excelOperateObject = excelOperateDictionary[handle];sheet = excelOperateObject.WorkBook.GetSheet(sheetName) == null ? excelOperateObject.WorkBook.CreateSheet(sheetName) : excelOperateObject.WorkBook.GetSheet(sheetName);row = sheet.GetRow(rowIndex) == null ? sheet.CreateRow(rowIndex) : sheet.GetRow(rowIndex);cell = row.GetCell(columnIndex) == null ? row.CreateCell(columnIndex) : row.GetCell(columnIndex);cell.SetCellValue(value);return ExecuteResult.Success();}catch (Exception ex){return ExecuteResult.Error(ex.Message);}}
(5)根据句柄,查找Excel操作对象,读取单元格值
///<summary>/// 文本单元格值获取///</summary>///<param name="sheetName">表格名称</param>///<param name="rowIndex">行数</param>///<param name="columnIndex">列数</param>///<returns>获取结果值</returns>public ExecuteResult<string> GetCellValue(int handle, string sheetName, int rowIndex, int columnIndex){try{ISheet sheet;IRow row;ICell cell;if (!excelOperateDictionary.ContainsKey(handle)) return ExecuteResult<string>.Error($"handle:{handle}不存在!");ExcelOperateObject excelOperateObject = excelOperateDictionary[handle];if (!File.Exists(excelOperateObject.FilePath)) return ExecuteResult<string>.Error("文件不存在");sheet = excelOperateObject.WorkBook.GetSheet(sheetName);if (sheet == null) return ExecuteResult<string>.Error($"表格名称{sheetName},不存在");row = sheet.GetRow(rowIndex);if (row == null) return ExecuteResult<string>.Error($"行{rowIndex},不存在");cell = row.GetCell(columnIndex);if (cell == null) return ExecuteResult<string>.Error($"列{cell},不存在");return ExecuteResult<string>.Success(cell.StringCellValue);}catch (Exception ex){return ExecuteResult<string>.Error(ex.Message);}}
(6)根据句柄,把值写入文档
public ExecuteResult WriteToFile(int handle){try{if (!excelOperateDictionary.ContainsKey(handle)) return ExecuteResult.Error($"handle:{handle}不存在!");ExcelOperateObject excelOperateObject = excelOperateDictionary[handle];excelOperateObject.WorkBook.Write(excelOperateObject.Fs);return ExecuteResult.Success();}catch (Exception ex){return ExecuteResult.Error(ex.Message);}}
(7)注销相关资源,避免内存泄漏
public void Dispose(int handle){if (!excelOperateDictionary.ContainsKey(handle)) return;ExcelOperateObject excelOperateObject = excelOperateDictionary[handle];excelOperateObject.Dispose();excelOperateDictionary.Remove(handle);}
三、使用
using ExcelToolLibCSharp;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace ConsoleApp2{internal class Program{staticvoidMain(string[] args){ExcelHelper excelHelper = new ExcelHelper();var handle = excelHelper.CreateOrOpenFile("D:\\test.xlsx");excelHelper.SetCellValue(handle.RetT, "Sheet1", 1, 1,"excel test");excelHelper.SetCellValue(handle.RetT, "Sheet1", 1, 1,"excel test");excelHelper.WriteToFile(handle.RetT);var res = excelHelper.GetCellValue(handle.RetT, "Sheet1", 1, 1);Console.WriteLine($"excel 读取数值:{res.RetT}");while (true) ;}}}
