using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System.Collections; using System.Reflection; namespace OSS.Tools.Excel; public static class ExcelFileExtension { /// /// 从Excel文件加载列表 /// /// /// Excel文件流 /// 标题定义所在行索引(从0开始) /// 数据起始行索引(从0开始) /// public static List LoadListFromExcelFile(this Stream stream, int titleDefineRowIndex = 0, int dataStartRowIndex = 1) where T : class, new() { return LoadListFromExcelFile(stream, string.Empty, null, titleDefineRowIndex, dataStartRowIndex); } /// /// 从Excel文件加载列表 /// /// /// Excel文件流 /// 执行sheet名称 /// 标题定义所在行索引(从0开始) /// 数据起始行索引(从0开始) /// public static List LoadListFromExcelFile(this Stream stream, string sheetName, int titleDefineRowIndex = 0, int dataStartRowIndex = 1) where T : class, new() { return LoadListFromExcelFile(stream, sheetName, null, titleDefineRowIndex, dataStartRowIndex); } /// /// 从Excel文件加载列表 /// /// /// Excel文件流 /// /// 行数据过滤方法(参数0,参数1,参数2) 。 /// 如果为空,值转化异常时会直接抛出; 如果不为空,值转化异常会被拦截并通过当前方法的 参数2 传递 /// 返回 true - 数据有效, 返回false - 数据无效 ///
--参数0:数据所在Excel行索引, ///
--参数1:转化后的行数据, ///
--参数2:列属性转化错误列表 - <Excel标题,错误信息> /// /// 标题定义所在行索引(从0开始) /// 数据起始行索引(从0开始) /// public static List LoadListFromExcelFile(this Stream stream, Func?, bool>? rowFilter, int titleDefineRowIndex = 0, int dataStartRowIndex = 1) where T : class, new() { return LoadListFromExcelFile(stream, string.Empty, rowFilter, titleDefineRowIndex, dataStartRowIndex); } /// /// 从Excel文件加载列表 /// /// /// Excel文件流 /// 执行sheet名称 /// /// 行数据过滤方法(参数0,参数1,参数2) 。 /// 如果为空,值转化异常时会直接抛出; 如果不为空,值转化异常会被拦截并通过当前方法的 参数2 传递 /// 返回 true - 数据有效, 返回false - 数据无效 ///
--参数0:数据所在Excel行索引, ///
--参数1:转化后的行数据, ///
--参数2:列属性转化错误列表 - <Excel标题,错误信息> /// /// 标题定义所在行索引(从0开始) /// 数据起始行索引(从0开始) /// public static List LoadListFromExcelFile(this Stream stream, string sheetName, Func?, bool>? rowFilter, int titleDefineRowIndex = 0, int dataStartRowIndex = 1) where T : class, new() { using var workbook = new XSSFWorkbook(stream); var sheet = string.IsNullOrEmpty(sheetName) ? workbook.GetSheetAt(0) : workbook.GetSheet(sheetName); var excelProperties = GetExcelPropertiesFromTitleRow(sheet, typeof(T), ref titleDefineRowIndex); var rowMaxIndex = sheet.LastRowNum; var resList = new List(rowMaxIndex); if (dataStartRowIndex < sheet.FirstRowNum) dataStartRowIndex = sheet.FirstRowNum; for (var rowIndex = dataStartRowIndex; rowIndex <= rowMaxIndex; rowIndex++) { var rowObj = sheet.GetRow(rowIndex); var item = GetRowItem(rowObj, rowIndex, excelProperties, rowFilter); if (item != null) { resList.Add(item); } } return resList; } private static T? GetRowItem(IRow? rowObj, int rowIndex, List colPropertyMap, Func?, bool>? rowFilter) where T : class, new() { if (rowObj == null) return null; var resItem = new T(); List? errors = null; foreach (var excelProperty in colPropertyMap) { var cell = rowObj.GetCell(excelProperty.ExcelColIndex); if (cell == null) continue; var valType = excelProperty.IsDictionary ? excelProperty.ItemProperty.PropertyType : excelProperty.Property.PropertyType; try { var cellVal = cell.GetCellValue(cell.CellType, valType); excelProperty.SetValue(resItem, cellVal); } catch { if (rowFilter == null) throw; errors ??= new List(); errors.Add( new ErrorPropertyMessage() { title = excelProperty.ExcelColTitle, err_msg = $"值({cell})转化类型({valType.Name})错误", property_name = excelProperty.Property.Name, column_index = excelProperty.ExcelColIndex }); } } if (rowFilter == null || rowFilter(rowIndex, resItem, errors)) return resItem; return null; } /// /// 获取Excel列和实体属性的映射关系实体 /// /// /// /// /// private static List GetExcelPropertiesFromTitleRow(ISheet sheet, IReflect entType, ref int titleDefineRow) { if (titleDefineRow < sheet.FirstRowNum) titleDefineRow = sheet.FirstRowNum; var properties = entType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty); var colProperties = new List(properties.Length); var sheetTitleRow = sheet.GetRow(titleDefineRow); var sheetColumnCount = sheetTitleRow.LastCellNum; foreach (var p in properties) { var attr = p.GetCustomAttribute(true); var isDic = attr?.IsDictionary ?? false; var titleKey = attr?.TitleName ?? p.Name; var isDictionary = attr is { IsDictionary: true }; var needFillLatest = p.GetCustomAttribute() != null; for (var i = 0; i < sheetColumnCount; i++) { if (colProperties.Any(c => c.ExcelColIndex == i)) continue; var excelTitle = sheetTitleRow.GetCell(i)?.ToString() ?? string.Empty; if ((!isDic && excelTitle != titleKey) || (isDic && !excelTitle.StartsWith(titleKey))) continue; var excelProperty = GetExcelProperty(excelTitle, i, p, isDictionary, needFillLatest); colProperties.Add(excelProperty); } } return colProperties; } private static ExcelProperty GetExcelProperty( string excelTitle, int index, PropertyInfo property, bool isDictionary, bool needFillByLatest) { var excelProperty = new ExcelProperty() { //TitleKey = titleKey, ExcelColIndex = index, ExcelColTitle = excelTitle, Property = property, NeedFillByLatestValue = needFillByLatest }; if (!isDictionary) return excelProperty; if (!property.PropertyType.IsAssignableTo(typeof(IDictionary)) || property.PropertyType.GetGenericArguments()[0] != typeof(string)) { throw new ArgumentException($"动态列属性({property.Name})必须是字典类型(且Key必须是string类型)"); } excelProperty.IsDictionary = isDictionary; excelProperty.ItemProperty = property.PropertyType.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance)!; return excelProperty; } /// /// 获取单元格值 /// 如果是计算类型,需要获取计算后的类型值 /// /// private static object? GetCellValue(this ICell? cell, CellType cellType, Type properType) { if (cell == null) return null; switch (cellType) { case CellType.Formula: return GetCellValue(cell, cell.CachedFormulaResultType, properType); case CellType.Numeric: if (properType == typeof(DateTime) || properType == typeof(DateTime?)) { return cell.DateCellValue; } return Convert.ChangeType(cell.NumericCellValue, properType); case CellType.Boolean: return Convert.ChangeType(cell.BooleanCellValue, properType); case CellType.String: return Convert.ChangeType(cell.StringCellValue, properType); case CellType.Unknown: case CellType.Blank: case CellType.Error: default: return Convert.ChangeType(cell.ToString(), properType); } } }