根据数据源自定义字段实现动态导出Excel

前言

  最近遇到了一个需求,需要导出各种订单数据,可能是因为懒吧,不想重新写查询然后导出数据;就有了下边的这些操作了。

具体实现方式

  1),定义一个泛型类来接收我们要导出的数据源,代码如下:

 1       /// 
 2         /// 传入数据,返回Excel流文件
 3         /// 
 4         /// 
 5         /// 数据源
 6         /// sheet名字
 7         /// 
 8         private async Task GetExcelMemoryStreams(List TSources, string sheetName = "sheet1")
 9         {
10             return await Task.Run(() =>
11             {
12                 MemoryStream ms = new MemoryStream();
13                 if (TSources.Any())
14                 {
15                     using (ExcelPackage package = new ExcelPackage())
16                     {
17                         ExcelWorksheet sheet = package.Workbook.Worksheets.Add(sheetName);
18 
19 
20                         //获取传进来的类型
21                         Type type = typeof(T);
22                         var propertiesList = type.GetProperties();
23 
24                         for (int row = 1; row <= TSources.Count() + 1; row++)
25                         {
26                             var index = 0;
27                             for (int cl = 1; cl <= propertiesList.Length; cl++)
28                             {
29                                 //获取备注名字
30                                 var displayName = propertiesList[cl - 1].GetExportExcelDisplayName();
31                                 //判断字段是否有自定义属性(ExportExcelColumnAttribute)
32                                 if (displayName != "false")
33                                 {
34                                     index++;
35                                     if (row == 1) //设置表头
36                                         sheet.Cells[row, index].Value = displayName;
37                                     else
38                                     {
39                                         //获取字段名字
40                                         var Name = propertiesList[cl - 1].Name;
41                                         //获取对应的值
42                                         var value = TSources[row - 2].GetType().GetProperty(Name)?.GetValue(TSources[row - 2])?.ToString();
43                                         sheet.Cells[row, index].Value = value;
44                                     }
45                                 }
46                             }
47                         }
48 
49                         //设置Excel列宽
50                         sheet.Cells.AutoFitColumns(1.5);
51 
52                         package.SaveAs(ms);
53                         ms.Position = 0;
54                     }
55                 }
56                 else
57                     throw new UserFriendlyException($"{sheetName}暂无数据!");
58 
59                 return ms;
60             });
61         }

  2),定义实体属性标识导出列,代码如下:

 1 /// 
 2     /// Excel导出用
 3     /// 
 4     [AttributeUsage(AttributeTargets.Property)]//标记只能用在属性上面
 5     public class ExportExcelColumnAttribute : Attribute
 6     {
 7         private string _Name = null;
 8 
 9         /// 
10         /// 构造涵数传入值 
11         /// 
12         /// 
13         public ExportExcelColumnAttribute(string Name)
14         {
15             this._Name = Name;
16         }
17 
18         public string GetDisplayName()
19         {
20             return _Name;
21         }
22     }

  3),定义获取该属性的备注名(Excel导出列名)方法,代码如下:

 1         /// 
 2         /// 获取属性设置的导出备注
 3         /// 
 4         /// 
 5         /// 
 6         public static string GetExportExcelDisplayName(this PropertyInfo prop)
 7         {
 8             if (prop.IsDefined(typeof(ExportExcelColumnAttribute), true))
 9             {
10                 ExportExcelColumnAttribute attribute = (ExportExcelColumnAttribute)prop.GetCustomAttribute(typeof(ExportExcelColumnAttribute), true);
11                 return attribute.GetDisplayName();
12             }
13             else
14             {
15                 return "false";
16             }
17         }

  4),在实体上标注要导出的字段

 1 public class GetServerOrderListDto : NullableIdDto<long>
 2     {
 3         /// 
 4         /// 订单编号
 5         ///  
 6         [ExportExcelColumn("订单编号")]
 7         public string OrdernNumber { get; set; }
 8 
 9         /// 
10         /// 赠购单id
11         /// 
12         public long? OrderID { get; set; }
13 
14         /// 
15         /// 赠送单号
16         /// 
17         [ExportExcelColumn("赠送单号")]
18         public string ComplimentaryOrderNumber { get; set; }
19         /// 
20         /// 收费单号
21         /// 
22         [ExportExcelColumn("收费单号")]
23         public string AdditionalOrdernNumber { get; set; }
24 
25         /// 
26         /// 客户名称
27         /// 
28         [ExportExcelColumn("客户名称")]
29         public string CompanyName { get; set; }
30 }       

  5),接口服务调用:

根据数据源自定义字段实现动态导出Excel_第1张图片

 

温馨提示

  a),上述第34行 index 作用主要是防止出现空白列,(实体列中不标识某列导出时(如上述实体 赠送订单ID),会显示空白列的问题:如下图情况:根据数据源自定义字段实现动态导出Excel_第2张图片);

  b),导出列会比较窄,我们需要设置自适应宽度: sheet.Cells.AutoFitColumns(1.5); 调整列宽;

  c),返回的内存流 MemoryStream 返回的时候需要将位置至为0(代码中:ms.Position = 0;);因为Read()方法是从当前流的Position属性的位置开始读,这就是为什么很多人测试的时候,刚刚写入内存的数据,Read()方法无法读取到内容的原因,因为刚刚写入内存之后,位置恰好是在最后一位了。Read()方法当然读不到。此方法强大之处在于,你可以从一个内存流中读出你想要的一个片段。

  d),using块的 ExcelPackage对象不需要手动释放,因为他继承了IDisposable,在使用完之后会自动释放;如下图:

根据数据源自定义字段实现动态导出Excel_第3张图片

  e),MemoryStream 是一个特例,MemoryStream中没有任何非托管资源,所以它的Dispose不调用也没关系;托管资源.Net会自动回收;

 

转载于:https://www.cnblogs.com/Jinfeng1213/p/11341529.html

你可能感兴趣的:(根据数据源自定义字段实现动态导出Excel)