最近新项目开始了,万能的excel又要上台工作。对于导出代码网上的确很多,合适的没有找到。
我们这边使用的ef + dapper 两套orm框架,他们有一个共同点,都是操作实体对象的。所以导出不能用万能的DataTable了。后面花了一些时间,集合众仙所长,写了一套单excel导入导出的方式。数据源为List<T>
public class Excel { /// <summary> /// 导出到sheet中 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="path"></param> /// <param name="list"></param> /// <param name="sheetName"></param> public static void Export<T>(string path, List<T> list, string sheetName = "sheet1") where T : class, new() { IWorkbook book = null; //this.gridView1.ExportToXls(sflg.FileName); //NPOI.xs book = new NPOI.HSSF.UserModel.HSSFWorkbook(); if (Path.GetExtension(path).ToLower() != ".xlsx") { book = new HSSFWorkbook(); } else { book = new XSSFWorkbook();//07 } ISheet sheet = book.CreateSheet("sheet"); // 添加表头 var pros = typeof(T).GetProperties(); IRow row = sheet.CreateRow(0); int index = 0; foreach (var p in pros) { string alias = AliasAttribute<T>(p); if (!string.IsNullOrEmpty(alias)) { ICell cell = row.CreateCell(index); cell.SetCellType(CellType.String); cell.SetCellValue(alias); int num = Encoding.GetEncoding(936).GetBytes(alias).Length; sheet.SetColumnWidth(index, num * 500); index++; } } // 添加数据 for (int i = 0; i < list.Count; i++) { index = 0; row = sheet.CreateRow(i + 1); foreach (var p in pros) { string alias = AliasAttribute<T>(p); if (!string.IsNullOrEmpty(alias)) { //var attributes = p.GetCustomAttributes(typeof(AliasAttribute), false); //var type = (AliasAttribute)attributes[0]; object value = p.GetValue(list[i], null); ICell cell = row.CreateCell(index); //cell.SetCellType(CellType.String); //cell.SetCellValue(value); SetCellValue(cell, p, value, book); index++; } } } // 写入 MemoryStream ms = new MemoryStream(); book.Write(ms); using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write)) { byte[] data = ms.ToArray(); fs.Write(data, 0, data.Length); fs.Flush(); } ms.Close(); ms.Dispose(); } private static string AliasAttribute<T>(PropertyInfo p) where T : class, new() { var attributes = p.GetCustomAttributes(typeof(AliasAttribute), false); AliasAttribute type = null; foreach (var o in attributes) { if (o is AliasAttribute) { type = o as AliasAttribute; } } return type?.Alias; } /// <summary> /// 读Excel单元格的数据 /// </summary> /// <param name="cell">Excel单元格</param> /// <param name="type">列数据类型</param> /// <param name="value"></param> /// <param name="book"></param> /// <returns>object 单元格数据</returns> private static void SetCellValue(ICell cell, PropertyInfo type, object value, IWorkbook book) { string dataType = type.PropertyType.FullName; switch (dataType) { case "System.String"://字符串类型 cell.SetCellValue(Convert.ToString(value)); break; case "System.DateTime"://日期类型 ICellStyle cellDateStyle = book.CreateCellStyle(); IDataFormat format = book.CreateDataFormat(); cellDateStyle.DataFormat = format.GetFormat("yyyy-MM-dd hh:mm:ss");//yyyy-mm-dd hh:mm:ss cell.CellStyle = cellDateStyle; cell.SetCellValue(Convert.ToDateTime(value)); break; case "System.Boolean"://布尔型 cell.SetCellValue(Convert.ToBoolean(value)); break; case "System.Int16"://整型 case "System.Int32": case "System.Int64": case "System.Byte": cell.SetCellValue(Convert.ToUInt64(value)); break; case "System.Decimal"://浮点型 case "System.Double": cell.SetCellValue(Convert.ToDouble(value)); break; case "System.DBNull"://空值处理 cell.SetCellValue(""); break; default: throw (new Exception(dataType + ":类型数据无法处理!")); } } /// <summary> /// 导入 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="path"></param> /// <param name="sheetIndex"></param> /// <returns></returns> public static List<T> Import<T>(string path, int sheetIndex = 0) where T : class, new() { List<T> list = new List<T>(); IWorkbook book; FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); if (Path.GetExtension(path).ToLower() != ".xlsx") { book = new HSSFWorkbook(fs); } else { book = new XSSFWorkbook(fs);//07 } ISheet sheet = book.GetSheetAt(sheetIndex); IRow row = sheet.GetRow(0); if (row == null) return list; int firstCellNum = row.FirstCellNum; int lastCellNum = row.LastCellNum; if (firstCellNum == lastCellNum) return list; //记录列号与属性名称对应关系 Dictionary<int, PropertyInfo> dictionary = new Dictionary<int, PropertyInfo>(); var pros = typeof(T).GetProperties(); for (int i = firstCellNum; i < lastCellNum; i++) { string name = row.GetCell(i).StringCellValue; foreach (var p in pros) { string alias = AliasAttribute<T>(p); if ((!string.IsNullOrEmpty(alias)) && name == alias) { dictionary.Add(i, p); break; } } } for (int i = 1; i <= sheet.LastRowNum; i++) { T t = new T(); row = sheet.GetRow(i); //for (int j = firstCellNum; j < lastCellNum; j++) foreach (var info in dictionary) { SetValue(row.GetCell(info.Key), info.Value, t); } list.Add(t); } return list; } /// <summary> /// 读Excel单元格的数据 /// </summary> /// <param name="cell">Excel单元格</param> /// <param name="type">列数据类型</param> /// <param name="obj"></param> /// <returns>object 单元格数据</returns> private static void SetValue(ICell cell, PropertyInfo type, object obj) { object cellValue; switch (cell.CellType) { case CellType.String: //文本 cellValue = cell.StringCellValue; break; case CellType.Numeric: //数值 if (cell.CellType == CellType.Numeric && DateUtil.IsCellDateFormatted(cell)) { cellValue = cell.DateCellValue; break; } cellValue = cell.NumericCellValue; break; case CellType.Boolean: //bool cellValue = cell.BooleanCellValue; break; case CellType.Blank: //空白 cellValue = ""; break; default: cellValue = null; break; } string dataType = type.PropertyType.FullName; if (dataType == "System.String") { type.SetValue(obj, cellValue, null); } else if (dataType == "System.DateTime") { DateTime pdt = Convert.ToDateTime(cellValue); type.SetValue(obj, pdt, null); } else if (dataType == "System.Boolean") { bool pb = Convert.ToBoolean(cellValue); type.SetValue(obj, pb, null); } else if (dataType == "System.Int16") { Int16 pi16 = Convert.ToInt16(cellValue); type.SetValue(obj, pi16, null); } else if (dataType == "System.Int32") { Int32 pi32 = Convert.ToInt32(cellValue); type.SetValue(obj, pi32, null); } else if (dataType == "System.Int64") { Int64 pi64 = Convert.ToInt64(cellValue); type.SetValue(obj, pi64, null); } else if (dataType == "System.Byte") { Byte pb = Convert.ToByte(cellValue); type.SetValue(obj, pb, null); } else if (dataType == "System.Decimal") { System.Decimal pd = Convert.ToDecimal(cellValue); type.SetValue(obj, pd, null); } else if (dataType == "System.Double") { double pd = Convert.ToDouble(cellValue); type.SetValue(obj, pd, null); } else { type.SetValue(obj, null, null); } } }
导出的中文名称是一个大问题,总不能把实体中的属性导出到excel中,定义中文属性又不符合”我们“的风格。没有无法解决的问题,我们用特性,看我们的大招
/// <summary> /// 导出excel列名 /// </summary> [AttributeUsage(AttributeTargets.Property)] public class AliasAttribute : Attribute { /// <summary> /// 属性别名 /// </summary> public string Alias { get; } /// <summary> /// 构造方法 /// </summary> /// <param name="alias">别名</param> public AliasAttribute(string alias) { Alias = alias; } }
代码写完了,后面简单测试一下。
[TestClass] public class ExcelTest { /// <summary> /// 导出 /// </summary> [TestMethod] public void Export() { List<ExcelTestM> listExcelTestMs = new List<ExcelTestM>(100); for (int i = 0; i < 100; i++) { listExcelTestMs.Add(new ExcelTestM() { Name = "张三" + i, Age = i, Address = "确认ws能够成功建立连接。如果ws无法正常建立连接,请参照", CreateDate = DateTime.Now, Ishides = true, Amount = i / 1.1, }); } Excel.Export(@"D:1.xlsx", listExcelTestMs); } /// <summary> /// 实体中只有部分列导出 /// 如果:只导出配置的列 /// </summary> [TestMethod] public void ExportPart() { List<ExcelTestM> listExcelTestMs = new List<ExcelTestM>(100); for (int i = 0; i < 100; i++) { listExcelTestMs.Add(new ExcelTestM() { Name = "张三" + i, Age = i, Address = "确认ws能够成功建立连接。如果ws无法正常建立连接,请参照", CreateDate = DateTime.Now, Ishides = true, Amount = i / 1.1, }); } Excel.Export(@"D:1.xlsx", listExcelTestMs); } /// <summary> /// 导入 /// </summary> [TestMethod] public void Import() { var list = Excel.Import<ExcelTestM>(@"D:1.xlsx"); } /// <summary> /// 导入 /// 1、EXCEL列多于实体 /// 2、实体列多于EXCEL /// 结果:取实体与EXCEL的交集 /// </summary> [TestMethod] public void ImportPart() { //1、EXCEL列多于实体 var list = Excel.Import<ExcelTestM>(@"D:1.xlsx"); //2、实体列多于EXCEL list = Excel.Import<ExcelTestM>(@"D:1.xlsx"); } } /// <summary> /// excel测试实体 /// </summary> public class ExcelTestM { /// <summary> /// 姓名 /// </summary> [Alias("姓名")] public string Name { get; set; } /// <summary> /// 年龄 /// </summary> [Alias("年龄")] public int Age { get; set; } /// <summary> /// 地址 /// </summary> [Alias("地址1111111111111111")] public string Address { get; set; } [Alias("日期")] public DateTime CreateDate { get; set; } public bool Ishides { get; set; } //[Alias("数据啊啊啊")] public double Amount { get; set; } }
感谢你能耐心的看到最后,这些代码没有添加错误处理,你在使用时,可以直接获取到错误信息,以提示用户。