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);
}
}
}