背景:
在工作中我们有一个很常见的业务场景:导出列表的数据,生成Excel,而使用NPOI生成Excel我们也会遇到一个问题,每遇到一个不同的类导出时都要生成不同的表头,行,列,但其实里面大部分代码都是冗余的,所以这时我就想到了一个点子,通过泛型和反射整一个通用的导出方法.
导出思路:
其实这个业务场景的原理很简单,首先我们需要查出数据并且将数据生成Excel文件再通过IO流保存在我们服务器中,最后再将完整的链接地址返回给前端,前端访问就直接下载下来了,还有一个问题则是解决那些冗余代码,这里就是我们上面提到的可以通过泛型和反射封装一个通用的导出方法或者整一个IEnumber<T>的扩展方法.
环境:
代码环境使用的是.Net 6.0,
Nuget包:
实现:
第一步,我们需要写一个泛型获取到泛型所有的字段名称和值的方法,用于生成表头,行,列做铺垫,代码如下:
/// <summary> /// 获取对象属性名称和值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <returns></returns> public IEnumerable<KeyValue> GetProperties<T>(T t) { var result = new List<KeyValue>(); System.Reflection.PropertyInfo[] properties = t.GetType().GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); foreach (System.Reflection.PropertyInfo item in properties) { string name = item.Name; //名称 object value = item?.GetValue(t, null) ?? string.Empty; //值 //if (item.PropertyType.IsValueType || item.PropertyType.Name.StartsWith("String")) //{ result.Add(new KeyValue() { Key = item.Name, Value = item.GetValue(t, null) }); //} } return result; }
第二步,封装一个导出Excel文件的方法,通过List<T>解决冗余的问题,并通过不同的类型生成不同的Excel并且通过IO流存入服务器中
/// <summary> /// 通用导出 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="entitys"></param> /// <param name="fileName"></param> /// <returns></returns> public string ExportExcel<T>(IEnumerable<T> entitys, string fileName) { if (entitys == null || !entitys.Any()) return "无数据"; XSSFWorkbook workBook = new XSSFWorkbook(); ISheet sheet1 = workBook.CreateSheet("Sheet1"); { var properties1 = GetProperties(entitys.ToList()[0]); //创建表头 var row = sheet1.CreateRow(0); for (var i = 0; i < properties1.Count(); i++) { var cell = row.CreateCell(i); cell.SetCellValue(properties1.ToList()[i].Key); } } //生成数据 int index = 1; foreach (var entity in entitys) { var properties = GetProperties(entity);//获取类中字段的名称和值 var row = sheet1.CreateRow(index); var list = properties.ToList(); for (var i = 0; i < properties.Count(); i++) { var cell = row.CreateCell(i); //var itemValue = list.FirstOrDefault(d => d.Key == heads[i]); cell.SetCellValue(list[i].Value?.ToString() ?? string.Empty); } index++; } string defaultPath = System.IO.Directory.GetCurrentDirectory() + "/wwwroot/upload/file"; if (!System.IO.Directory.Exists(defaultPath)) { System.IO.Directory.CreateDirectory(defaultPath);//不存在就创建文件夹 } //创建文件 var file = new FileStream(defaultPath + "/" + fileName, FileMode.Create); workBook.Write(file); file.Close(); return "upload/file/" + fileName; }
结尾:
需要注意的是,生成的表头,为类字段的名称,导出的Dto字段名称可以写成中文的,如果不想在代码中出现中文,可以再加一个List<string> heads//表头的参数,更改掉生成表头那段代码.
最后希望这篇文章可以帮到你哦,文中有不足的地方也欢迎指正嘻嘻