C#_.Net Core 3.1 WebAPI_Excel数据读取与写入_自定义解析封装类
本篇博客园是被任务所逼,而已有的使用nopi技术的文档技术经验又不支持我需要的应对各种复杂需求的苛刻要求,只能自己造轮子封装了,由于需要应对很多总类型的数据采集需求,因此有了本篇博客的代码封装,下面一点点介绍吧: 项目框架:.net Core 3.1? ? Nuget包:DotNetCore.NPOI 1.2.2 ? 收集excel你有没有遇到过一下痛点: 1-需要收集指定行标题位置的数据,我的标题行不一定在第一行。? 这个和我的csv的文档需求是一致的 2-需要采集指定单元格位置的数据生成一个对象,而不是一个列表。? ?这里我的方案是制定一个单元格映射类解决问题。? 单元格映射类,支持表达式数据采集(我可能需要一个单元格的数据+另一个单元格的数据作为一个属性等等) 3-应对不规范标题无法转出字符串进行映射时,能不能通过制定标题的列下标建立对应关系,进行列表数据采集呢?? ?本博客同时支持标题字符串数据采集和标题下标数据采集,这个就牛逼了。 4-存储含有表达式的数据,这个并不是难点,由于很重要,就在这里列一下 5-应对Excel模板文件的数据指定位置填入数据,该位置可能会变动的解决方案。本文为了应对该情况,借助了单元格映射关系,添加了模板参数名的属性处理,可以应对模板文件调整时的位置变动问题。 6-一个能同时处理excel新老版本(.xls和.xlsx),一个指定excel位置保存数据,保存含有表达式的数据,一个可以将多个不同的数据组合存放到一个excel中的需求都可以满足。? ? ? 痛点大概就是上面这些了,下面写主要代码吧,供大家参考,不过封装的类方法有点多:
本文借助了NPOI程序包做了业务封装: ? 1-主要封装类-ExcelHelper: 该类包含很多辅助功能:比如自动帮助寻找含有指定标题名所在的位置、表达式元素A1,B2对应单元格位置的解析等等: ? /// <summary> /// EXCEL帮助类 </summary> <typeparam name="T">泛型类</typeparam> <typeparam name="TCollection">泛型类集合</typeparam> public class ExcelHelper { public ILogger Logger { get; set; } public ExcelHelper(ILogger<ExcelHelper> logger) { this.Logger = logger; } #region 创建工作表 <summary> 将列表数据生成工作表 </summary> <param name="tList">要导出的数据集</param> <param name="fieldNameAndShowNameDic">键值对集合(键:字段名,值:显示名称)<param name="workbook">更新时添加:要更新的工作表<param name="sheetName">指定要创建的sheet名称时添加<param name="excelFileDescription">读取或插入定制需求时添加<returns></returns> public IWorkbook CreateOrUpdateWorkbook<T>(List<T> tList,Dictionary<string,string> fieldNameAndShowNameDic,IWorkbook workbook = null,1)">string sheetName = "sheet1",ExcelFileDescription excelFileDescription = null) where T : new() { List<ExcelTitleFieldMapper> titleMapperList = ExcelTitleFieldMapper.GetModelFieldMapper<T>(fieldNameAndShowNameDic); workbook = this.CreateOrUpdateWorkbook<T>(tList,titleMapperList,workbook,sheetName,excelFileDescription); return workbook; } 将列表数据生成工作表(T的属性需要添加:属性名列名映射关系) (); workbook = workbook; } private IWorkbook CreateOrUpdateWorkbook<T>(List<T> tList,List<ExcelTitleFieldMapper> titleMapperList,IWorkbook workbook,1)">string sheetName,1)">null) { //xls文件格式属于老版本文件,一个sheet最多保存65536行;而xlsx属于新版文件类型; Excel 07 - 2003一个工作表最多可有65536行,行用数字1—65536表示; 最多可有256列,列用英文字母A—Z,AA—AZ,BA—BZ,……,IA—IV表示;一个工作簿中最多含有255个工作表,默认情况下是三个工作表; Excel 2007及以后版本,一个工作表最多可有1048576行,16384列; if (workbook == ) { workbook = XSSFWorkbook(); workbook = new HSSFWorkbook(); } ISheet worksheet = ; if (workbook.GetSheetIndex(sheetName) >= 0) { worksheet = workbook.GetSheet(sheetName); } else { worksheet = workbook.CreateSheet(sheetName); } IRow row1 = ; ICell cell = ; int defaultBeginTitleIndex = if (excelFileDescription != ) { defaultBeginTitleIndex = excelFileDescription.TitleRowIndex; } PropertyInfo propertyInfo = ; T t = default(T); int tCount = tList.Count; int currentRowIndex = int dataIndex = do { row1 = worksheet.GetRow(currentRowIndex); if (row1 == ) { row1 = worksheet.CreateRow(currentRowIndex); } if (currentRowIndex >= defaultBeginTitleIndex) { 到达标题行 if (currentRowIndex == defaultBeginTitleIndex) { int cellIndex = ; foreach (var titleMapper in titleMapperList) { cell = row1.GetCell(cellIndex); if (cell == ) { cell = row1.CreateCell(cellIndex); } this.SetCellValue(cell,titleMapper.ExcelTitle,outputFormat: ); cellIndex++; } } 到达内容行 { dataIndex = currentRowIndex - defaultBeginTitleIndex - 1if (dataIndex <= tCount - ) { t = tList[dataIndex]; ; titleMapperList) { propertyInfo = titleMapper.PropertyInfo; cell = row1.GetCell(cellIndex); ) { cell = row1.CreateCell(cellIndex); } this.SetCellValue<T>(cell,t,titleMapper); cellIndex++; } 重要:设置行宽度自适应(大批量添加数据时,该行代码需要注释,否则会极大减缓Excel添加行的速度!) worksheet.AutoSizeColumn(i,true); } } } currentRowIndex++; } while (dataIndex < tCount - ); 设置表达式重算(如果不添加该代码,表达式更新不出结果值) worksheet.ForceFormulaRecalculation = true workbook; } 将单元格数据列表生成工作表 <param name="commonCellList">所有的单元格数据列表public IWorkbook CreateOrUpdateWorkbook(CommonCellModelColl commonCellList,1)">" workbook.CreateSheet(sheetName); } 设置首列显示 IRow row1 = int rowIndex = int maxRowIndex = commonCellList.Max(m => m.ColumnIndex); int columnIndex = int maxColumnIndex = ; Dictionary<int,CommonCellModel> rowColumnIndexCellDIC = object cellValue = { rowColumnIndexCellDIC = commonCellList.GetRawCellList(rowIndex).ToDictionary(m => m.ColumnIndex); maxColumnIndex = rowColumnIndexCellDIC.Count > 0 ? rowColumnIndexCellDIC.Keys.Max() : ; if (rowColumnIndexCellDIC != null && rowColumnIndexCellDIC.Count > worksheet.GetRow(rowIndex); ) { row1 = worksheet.CreateRow(rowIndex); } columnIndex = ; { cell = row1.GetCell(columnIndex); ) { cell = row1.CreateCell(columnIndex); } if (rowColumnIndexCellDIC.ContainsKey(columnIndex)) { cellValue = rowColumnIndexCellDIC[columnIndex].CellValue; ,rowColumnIndexCellDIC[columnIndex].IsCellFormula); } columnIndex++; } while (columnIndex <= maxColumnIndex); } rowIndex++; } while (rowIndex <= maxRowIndex); 更新模板文件数据:将使用单元格映射的数据T存入模板文件中 <param name="filePath"><param name="t">添加了单元格参数映射的数据对象public IWorkbook CreateOrUpdateWorkbook<T>(string filePath,T t) { 该方法默认替换模板数据在首个sheet里 IWorkbook workbook = ; CommonCellModelColl commonCellColl = this._ReadCellList(filePath,1)">out workbook); ISheet worksheet = workbook.GetSheetAt(获取t的单元格映射列表 Dictionary< m.CellParamName); var rows = worksheet.GetRowEnumerator(); IRow row; ICell cell; cellValue; ExcelCellFieldMapper cellMapper; while (rows.MoveNext()) { row = (XSSFRow)rows.Current; int cellCount = row.Cells.Count; for (int i = 0; i < cellCount; i++) { cell = row.Cells[i]; cellValue = cell.ToString(); (tParamMapperDic.ContainsKey(cellValue)) { cellMapper = tParamMapperDic[cellValue]; if (tParamMapperDic.Count > ) { 循环所有单元格数据替换指定变量数据 var cellItem commonCellColl) { cellValue = cellItem.CellValue.ToString(); (tParamMapperDic.ContainsKey(cellValue)) { cellItem.CellValue = tParamMapperDic[cellValue].PropertyInfo.GetValue(t); } } } #endregion #region 保存工作表到文件 保存Workbook数据为文件 <param name="workbook"></param> <param name="fileDirectoryPath"></param> <param name="fileName"></param> void SaveWorkbookToFile(IWorkbook workbook,1)"> filePath) { Excel 2007及以后版本,一个工作表最多可有1048576行,16384列; MemoryStream ms = MemoryStream(); 这句代码非常重要,如果不加,会报:打开的EXCEL格式与扩展名指定的格式不一致 ms.Seek(byte[] myByteArray = ms.GetBuffer(); string fileDirectoryPath = filePath.Split('')[]; if (!Directory.Exists(fileDirectoryPath)) { Directory.CreateDirectory(fileDirectoryPath); } string fileName = filePath.Replace(fileDirectoryPath,"" (File.Exists(filePath)) { File.Delete(filePath); } File.WriteAllBytes(filePath,myByteArray); } 保存workbook到字节流中(提供给API接口使用) byte[] SaveWorkbookToByte(IWorkbook workbook) { MemoryStream stream = MemoryStream(); stream.Seek(byte[] byteArray = stream.GetBuffer(); byteArray; } #region 读取Excel数据 读取Excel数据1_手动提供属性信息和标题对应关系 <typeparam name="T"></typeparam> <param name="filePath"></param> <param name="fieldNameAndShowNameDic"></param> <param name="excelFileDescription"></param> public List<T> ReadTitleDataList<T>(string filePath,ExcelFileDescription excelFileDescription) () { 标题属性字典列表 List<ExcelTitleFieldMapper> titleMapperList = ExcelTitleFieldMapper.GetModelFieldMapper<T>(fieldNameAndShowNameDic); List<T> tList = this._GetTList<T>(filePath,1)">return tList ?? new List<T>(); } 读取Excel数据2_使用Excel标记特性和文件描述自动创建关系 (); List<T> tList = ); } private List<T> _GetTList<T>(() { List<T> tList = 500 * 10000); T t = using (FileStream fileStream = FileStream(filePath,FileMode.Open,FileAccess.Read)) { IWorkbook workbook = ; IFormulaEvaluator formulaEvaluator = try { workbook = XSSFWorkbook(fileStream); formulaEvaluator = XSSFFormulaEvaluator(workbook); } catch (Exception) { workbook = HSSFWorkbook(fileStream); formulaEvaluator = HSSFFormulaEvaluator(workbook); } int sheetCount = workbook.NumberOfSheets; int currentSheetIndex = ; int currentSheetRowTitleIndex = - { var sheet = workbook.GetSheetAt(currentSheetIndex); 标题下标属性字典 Dictionary<new Dictionary<); 如果没有设置标题行,则通过自动查找方法获取 if (excelFileDescription.TitleRowIndex < ) { string[] titleArray = titleMapperList.Select(m => m.ExcelTitle).ToArray(); currentSheetRowTitleIndex = this.GetSheetTitleIndex(sheet,titleArray); } { currentSheetRowTitleIndex = excelFileDescription.TitleRowIndex; } sheet.GetRowEnumerator(); bool isHaveTitleIndex = false含有Excel行下标 if (titleMapperList.Count > 0 && titleMapperList[0].ExcelTitleIndex >= ) { isHaveTitleIndex = ; titleMapperList) { sheetTitleIndexPropertyDic.Add(titleMapper.ExcelTitleIndex,titleMapper); } } PropertyInfo propertyInfo = ; (rows.MoveNext()) { IRow row = (IRow)rows.Current; currentRowIndex = row.RowNum; 到达标题行 if (isHaveTitleIndex == false && currentRowIndex == currentSheetRowTitleIndex) { ICell cell = string cellValue = ; Dictionary< m.ExcelTitle); 0; i < row.Cells.Count; i++ row.Cells[i]; cellValue = cell.StringCellValue; (titleMapperDic.ContainsKey(cellValue)) { sheetTitleIndexPropertyDic.Add(i,titleMapperDic[cellValue]); } } } 到达内容行 if (currentRowIndex > currentSheetRowTitleIndex) { t = T(); ExcelTitleFieldMapper excelTitleFieldMapper = var titleIndexItem sheetTitleIndexPropertyDic) { ICell cell = row.GetCell(titleIndexItem.Key); excelTitleFieldMapper = titleIndexItem.Value; 没有数据的单元格默认为null string cellValue = cell?.ToString() ?? ; propertyInfo = excelTitleFieldMapper.PropertyInfo; { (excelTitleFieldMapper.IsCheckContentEmpty) { if (.IsNullOrEmpty(cellValue)) { t = (T); break; } } if (excelTitleFieldMapper.IsCoordinateExpress || cell.CellType == CellType.Formula) { 读取含有表达式的单元格值 cellValue = formulaEvaluator.Evaluate(cell).StringValue; propertyInfo.SetValue(t,Convert.ChangeType(cellValue,propertyInfo.PropertyType)); } else (propertyInfo.PropertyType.IsEnum) { object enumObj = propertyInfo.PropertyType.InvokeMember(cellValue,BindingFlags.GetField,1)">); propertyInfo.SetValue(t,Convert.ChangeType(enumObj,1)"> { propertyInfo.SetValue(t,propertyInfo.PropertyType)); } } (Exception e) { this.Logger.LogError($文件_{filePath}读取{currentRowIndex + 1}行内容失败!); t = (T); ; } } if (t != ) { tList.Add(t); } } } currentSheetIndex++; } while (currentSheetIndex + 1 <= sheetCount); } 读取文件的所有单元格数据 文件路径public CommonCellModelColl ReadCellList( filePath) { IWorkbook workbook = ; CommonCellModelColl commonCellColl = workbook); commonCellColl; } IWorkbook workbook) { CommonCellModelColl commonCellColl = commonCellColl; } private CommonCellModelColl _ReadCellList(new CommonCellModelColl(); CommonCellModel cellModel = ; workbook = XSSFWorkbook(fileStream); } HSSFWorkbook(fileStream); } var sheet = workbook.GetSheetAt(); sheet.GetRowEnumerator(); List<ICell> cellList = ; ICell cell = 从第1行数据开始获取 (rows.MoveNext()) { IRow row = (IRow)rows.Current; cellList = row.Cells; cellList.Count; ) { cell = cellList[i]; cellModel = CommonCellModel { RowIndex = row.RowNum,ColumnIndex = i,CellValue = cell.ToString(),IsCellFormula = cell.CellType == CellType.Formula ? true : }; commonCellColl.Add(cellModel); } } } 获取文件单元格数据对象 T的属性必须标记了ExcelCellAttribute</typeparam> 文建路径public T ReadCellData<T>(string filePath) () { T t = T(); this.Logger.LogInformation($开始读取{filePath}的数据...); CommonCellModelColl commonCellColl = .ReadCellList(filePath); Dictionary<PropertyInfo,ExcelCellFieldMapper> propertyMapperDic = ExcelCellFieldMapper.GetModelFieldMapper<T>().ToDictionary(m => m.PropertyInfo); string cellExpress = string pValue = ; PropertyInfo propertyInfo = var item propertyMapperDic) { cellExpress = item.Value.CellCoordinateExpress; propertyInfo = item.Key; pValue = .GetVByExpress(cellExpress,propertyInfo,commonCellColl).ToString(); .IsNullOrEmpty(pValue)) { propertyInfo.SetValue(t,Convert.ChangeType(pValue,propertyInfo.PropertyType)); } } t; } 获取文件首个sheet的标题位置 T必须做了标题映射int FileFirstSheetTitleIndex<T>(int titleIndex = (File.Exists(filePath)) { { workbook = XSSFWorkbook(fileStream); } (Exception) { workbook = HSSFWorkbook(fileStream); } string[] titleArray = ExcelTitleFieldMapper.GetModelFieldMapper<T>().Select(m => m.ExcelTitle).ToArray(); ISheet sheet = workbook.GetSheetAt(); titleIndex = titleIndex; } <param name="titleNames"></param> int FileFirstSheetTitleIndex(params [] titleNames) { HSSFWorkbook(fileStream); } ISheet sheet = workbook.GetSheetAt( titleIndex; } #region 辅助方法 返回单元格坐标横坐标 <param name="cellPoint">单元格坐标(A1,B15...)<param name="columnIndex">带回:纵坐标private int GetValueByZM(string cellPoint,1)">out int columnIndex) { ; columnIndex = ; Regex columnIndexRegex = new Regex([a-zA-Z]+string columnZM = columnIndexRegex.Match(cellPoint).Value; rowIndex = Convert.ToInt32(cellPoint.Replace(columnZM,1)">"")) - int zmLen = .IsNullOrEmpty(columnZM)) { zmLen = columnZM.Length; } int i = zmLen - 1; i > -1; i--) { columnIndex += (int)Math.Pow((int)columnZM[i] - 64,(zmLen - i)); } columnIndex = columnIndex - rowIndex; } 根据单元格表达式和单元格数据集获取数据 <param name="cellExpress">单元格表达式<param name="commonCellColl">单元格数据集object GetVByExpress( cellExpress,PropertyInfo propertyInfo,CommonCellModelColl commonCellColl) { object value = 含有单元格表达式的取表达式值,没有表达式的取单元格字符串 .IsNullOrEmpty(cellExpress)) { MatchCollection matchCollection = Regex.Matches(cellExpress,1)">w+string point = ; System.Data.DataTable dt = System.Data.DataTable(); matchCollection) { point = item.ToString(); rowIndex = this.GetValueByZM(point,1)"> columnIndex); cellValue = commonCellColl[rowIndex,columnIndex]?.CellValue?.ToString() ?? if (propertyInfo.PropertyType == typeof(decimal) || propertyInfo.PropertyType == double) || propertyInfo.PropertyType == )) { .IsNullOrEmpty(cellValue)) { cellValue = cellValue.Replace(); } { cellValue = 0 { cellValue = $'{cellValue}'; } cellExpress = cellExpress.Replace(item.ToString(),cellValue); } 执行字符和数字的表达式计算(字符需要使用单引号包裹,数字需要移除逗号) value = dt.Compute(cellExpress,1)">); } return value ?? ; } 将数据放入单元格中 <param name="cell">单元格对象泛型类数据<param name="cellFieldMapper">单元格映射信息</param> void SetCellValue<T>(ICell cell,T t,ExcelCellFieldMapper cellFieldMapper) { object cellValue = cellFieldMapper.PropertyInfo.GetValue(t); .OutputFormat); } <param name="cellValue">数据<param name="outputFormat">格式化字符串<param name="isCoordinateExpress">是否是表达式数据void SetCellValue(ICell cell,1)">object cellValue,1)">string outputFormat,1)">bool isCoordinateExpress = if (cell != (isCoordinateExpress) { cell.SetCellFormula(cellValue.ToString()); } .IsNullOrEmpty(outputFormat)) { string formatValue = ; IFormatProvider formatProvider = if (cellValue is DateTime) { formatProvider = DateTimeFormatInfo(); ((DateTimeFormatInfo)formatProvider).ShortDatePattern = outputFormat; } formatValue = ((IFormattable)cellValue).ToString(outputFormat,formatProvider); cell.SetCellValue(formatValue); } { is decimal || cellValue double || cellValue ) { cell.SetCellValue(Convert.ToDouble(cellValue)); } DateTime) { cell.SetCellValue((DateTime)cellValue); } bool) { cell.SetCellValue(()cellValue); } { cell.SetCellValue(cellValue.ToString()); } } } } } 根据标题名称获取标题行下标位置 <param name="sheet">要查找的sheet<param name="titleNames">标题名称int GetSheetTitleIndex(ISheet sheet,1)">int titleIndex = -if (sheet != null && titleNames != null && titleNames.Length > ; List<string> rowValueList = row.Cells; rowValueList = new List<string>(cellList.Count); var cell cellList) { rowValueList.Add(cell.ToString()); } bool isTitle = var title titleNames) { rowValueList.Contains(title)) { isTitle = (isTitle) { titleIndex = row.RowNum; ; } } } #endregion } ? 2-自定义单元格类:
CommonCellModel { public CommonCellModel() { } public CommonCellModel(int rowIndex,1)">int columnIndex,1)">bool isCellFormula = this.RowIndex = rowIndex; this.ColumnIndex = columnIndex; this.CellValue = cellValue; this.IsCellFormula = isCellFormula; } int RowIndex { int ColumnIndex { object CellValue { ; } 是否是单元格公式 </summary> bool IsCellFormula { ; } } class CommonCellModelColl : List<CommonCellModel>,IList<CommonCellModel> { CommonCellModelColl() { } public CommonCellModelColl(int capacity) : base(capacity) { } 根据行下标,列下标获取单元格数据 <param name="rowIndex"></param> <param name="columnIndex"></param> public CommonCellModel this[ columnIndex] { get { CommonCellModel cell = this.FirstOrDefault(m => m.RowIndex == rowIndex && m.ColumnIndex == columnIndex); cell; } ) { cell.CellValue = value.CellValue; } } } 所有一行所有的单元格数据 <param name="rowIndex">行下标public List<CommonCellModel> GetRawCellList( rowIndex) { List<CommonCellModel> cellList = ; cellList = this.FindAll(m => m.RowIndex == rowIndex); return cellList ?? new List<CommonCellModel>( 所有一列所有的单元格数据 列下标public List<CommonCellModel> GetColumnCellList( columnIndex) { List<CommonCellModel> cellList = this.FindAll(m => m.ColumnIndex == columnIndex); ); } } ? 3-单元格特性类:
Excel单元格标记特性 </summary> [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property,AllowMultiple = )] ExcelCellAttribute : System.Attribute { 该参数用于收集数据存于固定位置的单元格数据(单元格坐标表达式(如:A1,B2,C1+C2...横坐标使用26进制字母,纵坐标使用十进制数字)) string CellCoordinateExpress { 该参数用于替换模板文件的预定义变量使用({A} {B}) string CellParamName { 字符输出格式(数字和日期类型需要) string OutputFormat { ; } public ExcelCellAttribute(string cellCoordinateExpress = string cellParamName = ) { CellCoordinateExpress = cellCoordinateExpress; CellParamName = cellParamName; } string cellCoordinateExpress,1)">string cellParamName,1)">string outputFormat) : (cellCoordinateExpress,cellParamName) { OutputFormat = outputFormat; } } ? 4-单元格属性映射帮助类:
单元格字段映射类 </summary> internal ExcelCellFieldMapper { 属性信息 public PropertyInfo PropertyInfo { 获取对应关系_T属性添加了单元格映射关系 static List<ExcelCellFieldMapper> GetModelFieldMapper<T>() { List<ExcelCellFieldMapper> fieldMapperList = new List<ExcelCellFieldMapper>(100); List<PropertyInfo> tPropertyInfoList = typeof(T).GetProperties().ToList(); ExcelCellAttribute cellExpress = tPropertyInfoList) { cellExpress = item.GetCustomAttribute<ExcelCellAttribute>(); if (cellExpress != ) { fieldMapperList.Add( ExcelCellFieldMapper { CellCoordinateExpress = cellExpress.CellCoordinateExpress,CellParamName = cellExpress.CellParamName,OutputFormat = cellExpress.OutputFormat,PropertyInfo = item }); } } fieldMapperList; } } ? 5-Excel文件描述类: ? ExcelFileDescription { 默认从第1行数据开始读取标题数据 public ExcelFileDescription() : this() { } public ExcelFileDescription( titleRowIndex) { this.TitleRowIndex = titleRowIndex; } 标题所在行位置(默认为0,没有标题填-1) int TitleRowIndex { ; } } ? 6-Excel标题特性类: ? Excel标题标记特性 ExcelTitleAttribute : System.Attribute { Excel行标题(标题和下标选择一个即可) string RowTitle { ; } Excel行下标(标题和下标选择一个即可,默认值-1) int RowTitleIndex { 单元格是否要检查空数据(true为检查,为空的行数据不添加) bool IsCheckContentEmpty { 是否是公式列 bool IsCoordinateExpress { 标题特性构造方法 <param name="title">标题<param name="isCheckEmpty">单元格是否要检查空数据是否是公式列是否有格式化输出要求public ExcelTitleAttribute(string title,1)">bool isCheckEmpty = false,1)">string outputFormat = ) { RowTitle = title; IsCheckContentEmpty = isCheckEmpty; IsCoordinateExpress = isCoordinateExpress; OutputFormat = outputFormat; RowTitleIndex = -; } int titleIndex,1)">) { RowTitleIndex = titleIndex; IsCheckContentEmpty = outputFormat; } } ? 7-Ecel标题属性映射帮助类: ? 标题字段映射类 ExcelTitleFieldMapper { 行标题 string ExcelTitle { 行标题下标位置 int ExcelTitleIndex { 是否要做行内容空检查 获取对应关系_T属性添加了标题映射关系 static List<ExcelTitleFieldMapper> GetModelFieldMapper<T>() { List<ExcelTitleFieldMapper> fieldMapperList = new List<ExcelTitleFieldMapper>((T).GetProperties().ToList(); ExcelTitleAttribute excelTitleAttribute = var tPropertyInfo tPropertyInfoList) { excelTitleAttribute = (ExcelTitleAttribute)tPropertyInfo.GetCustomAttribute((ExcelTitleAttribute)); if (excelTitleAttribute != ExcelTitleFieldMapper { PropertyInfo = tPropertyInfo,ExcelTitle = excelTitleAttribute.RowTitle,ExcelTitleIndex = excelTitleAttribute.RowTitleIndex,IsCheckContentEmpty = excelTitleAttribute.IsCheckContentEmpty,1)"> excelTitleAttribute.OutputFormat,IsCoordinateExpress = excelTitleAttribute.IsCoordinateExpress }); } } fieldMapperList; } 获取对应关系_手动提供映射关系 static List<ExcelTitleFieldMapper> GetModelFieldMapper<T>(Dictionary< fieldNameAndShowNameDic) { List<ExcelTitleFieldMapper> fieldMapperList = (T).GetProperties().ToList(); PropertyInfo propertyInfo = fieldNameAndShowNameDic) { propertyInfo = tPropertyInfoList.Find(m => m.Name.Equals(item.Key,StringComparison.OrdinalIgnoreCase)); fieldMapperList.Add( ExcelTitleFieldMapper { PropertyInfo = propertyInfo,1)"> item.Value,ExcelTitleIndex = -= }); } 获取对应关系_未提供(默认属性名和标题名一致) static List<ExcelTitleFieldMapper> GetModelDefaultFieldMapper<T>(T).GetProperties().ToList(); tPropertyInfoList) { fieldMapperList.Add( item,1)"> item.Name,1)"> fieldMapperList; } } ? 示例代码1-单元格映射类:
账户_多币种交易报表_数据源 AccountMultiCurrencyTransactionSource { 期初 </summary> [ExcelCellAttribute(B9)] decimal BeginingBalance { 收款 B19+C19decimal TotalTransactionPrice { 收到非EBay款项_主要指从其他账户转给当前账户的钱 B21+C21decimal TransferAccountInPrice { 退款(客户不提交争议直接退款) B23+C23decimal TotalRefundPrice { 手续费 B25+C25decimal TotalFeePrice { 争议退款 B37+C37decimal TotalChargebackRefundPrice { 转账与提现(币种转换) B45+C45decimal CurrencyChangePrice { 转账与提现(转账到paypal账户)_提现失败退回金额 B47+C47decimal CashWithdrawalInPrice { 转账与提现(从paypal账户转账)_提现金额 B49+C49decimal CashWithdrawalOutPrice { 购物_主要指从当前账户转给其他账户的钱 B51+C51decimal TransferAccountOutPrice { 其他活动 B85+C85decimal OtherPrice { 期末 C9decimal EndingBalance { ; } } ? 示例代码2-标题映射类(标题映射分类字符串映射和下标位置映射,这里使用下标位置映射):
美元币种转换_数据源 CurrencyChangeUSDSource { 日期[y/M/d] </summary> [ExcelTitleAttribute(0,1)">public DateTime Date { 类型 1,1)">string Type { 交易号 2string TX { 商家/接收人姓名地址第1行地址第2行/区发款账户名称_发款账户简称 3string SendedOrReceivedName { 电子邮件编号_发款账户全称 4string SendedOrReceivedAccountName { 币种 5string CurrencyCode { 总额 6decimal TotalPrice { 净额 7decimal NetPrice { 费用 8decimal FeePrice { ; } }
示例代码3-模板文件数据替换-单元格映射类:
多账户美元汇总金额_最终模板使用展示类 AccountUSDSummaryTransaction { </summary> [ExcelCellAttribute(cellParamName: {DLZ_BeginingBalance}decimal DLZ_BeginingBalance { {DLZ_TotalTransactionPrice}decimal DLZ_TotalTransactionPrice { {DLZ_TransferAccountInPrice}decimal DLZ_TransferAccountInPrice { {DLZ_TotalRefundPrice}decimal DLZ_TotalRefundPrice { {DLZ_TotalFeePrice}decimal DLZ_TotalFeePrice { {DLZ_TotalChargebackRefundPrice}decimal DLZ_TotalChargebackRefundPrice { {DLZ_CurrencyChangePrice}decimal DLZ_CurrencyChangePrice { {DLZ_CashWithdrawalInPrice}decimal DLZ_CashWithdrawalInPrice { {DLZ_CashWithdrawalOutPrice}decimal DLZ_CashWithdrawalOutPrice { {DLZ_TransferAccountOutPrice}decimal DLZ_TransferAccountOutPrice { {DLZ_OtherPrice}decimal DLZ_OtherPrice { {DLZ_EndingBalance}decimal DLZ_EndingBalance { { decimal result = this.DLZ_BeginingBalance + this.DLZ_TotalTransactionPrice + .DLZ_TransferAccountInPrice + this.DLZ_TotalRefundPrice + this.DLZ_TotalFeePrice + this.DLZ_TotalChargebackRefundPrice + .DLZ_CurrencyChangePrice + this.DLZ_CashWithdrawalInPrice + this.DLZ_CashWithdrawalOutPrice + this.DLZ_TransferAccountOutPrice + .DLZ_OtherPrice; result; } } 期末汇率差 {DLZ_EndingBalanceDifferenceValue} DLZ_EndingBalanceDifferenceValue { return this.DLZ_RealRateEndingBalance - .DLZ_EndingBalance; } } 真实汇率计算的期末余额 {DLZ_RealRateEndingBalance}decimal DLZ_RealRateEndingBalance { ; } } ? 示例代码4-存储多个数据源到一个Excel中(这里我是保存到了不同的sheet页里,当然也可以保持到同一个sheet的不同位置):
IWorkbook workbook = ; workbook = ExcelHelper.CreateOrUpdateWorkbook(dlzShopList,1)">独立站); workbook = ExcelHelper.CreateOrUpdateWorkbook(ebayShopList,1)">EBay); ExcelHelper.SaveWorkbookToFile(workbook,ConfigSetting.SaveReceivedNonEBayReportFile); ? 示例代码5-读取一个标题位置在第8行的标题行数据: ? ExcelHelper.ReadTitleDataList<T>("文件路径",1)">new ExcelFileDescription(7))
? 示例代码6-使用.net core API控制器方法返回文件流数据: [Route(api/[controller])] [ApiController] TestController : ControllerBase { public ExcelHelper ExcelHelper { TestController(ExcelHelper excelHelper) { this.ExcelHelper = excelHelper; } [HttpGet] [Route(async Task<ActionResult> Test() { IWorkbook workbook = ; CommonCellModelColl accountCellColl = 10 * ); var i = 0; i < 10; i++) { accountCellColl.Add(new CommonCellModel(第" + i + 列")); 标题行数据 } 最后循环数据列: vList 循环加到结合中去。 int beginRowIndex = ; List<dynamic> vList = dynamic>(); vList.Add(new { a = 2,b = }); vList.Add(3,1)"> }); int objPropertyCount = 2; 这里需要用反射获取 0; i < vList.Count; i++int j = 0; j < objPropertyCount; j++) { 值这里实际应根据属性反射获取属性值 int testValue = if (j == ) { testValue = ((dynamic)vList[i]).a; } { testValue = (()vList[i]).b; } accountCellColl.Add(new CommonCellModel(beginRowIndex,j,1)">测试数据" + testValue)); 内容行数据 } beginRowIndex++; } workbook = this.ExcelHelper.CreateOrUpdateWorkbook(accountCellColl,1)">生成字节流 byte[] myFileByteArray = .ExcelHelper.SaveWorkbookToByte(workbook); 设置导出文件名 this.Response.Headers.Add(content-dispositionattachment;filename=test.xlsawait this.Response.Body.WriteAsync(myFileByteArray,myFileByteArray.Length); return Ok(success); } } ? ?
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- ASP.NET站点移动到IIS7会导致页面输出中出现乱码
- asp.net-mvc – 不能删除数据库,因为它目前正在使用MVC
- asp.net – SelectedDate不适用于Calendar对象
- 使用ASP.NET DataBinding的C#4.0动态对象
- 浅谈ASP.NET配置文件加密
- 单元测试 – 有没有办法单元测试在视图中设置的ASP.NET MVC
- asp.net – 基类包含字段“ScriptManager1”,但其类型(Syst
- asp.net – 如何将MVC空项目转换为Web API?
- 有没有办法在ASP.NET中访问当前会话的集合?
- asp.net – 在单独的配置文件中移出web.config
- 全局导入/使用.NET中的别名
- asp.net-mvc – ASP.NET MVC控制器的[Authorize]
- asp.net – 如何保护应用程序池免受会话序列化异
- asp.net – 动态地将ASP控件添加到表中
- asp.net-mvc-2 – 从MCV2视图中的模型集合中读取
- asp.net-mvc – 为什么ValidateInput(False)不工
- asp.net-mvc – 使用源代码的Real World ASP.NET
- asp.net – 我可以在一个Web项目中拥有多个web.c
- asp.net – 在.NET 4.5中混合使用Windows和Forms
- asp.net-mvc-4 – 使用KNOCKOUT.JS和ASP.NET MVC