zoukankan      html  css  js  c++  java
  • 根据数据源自定义字段实现动态导出Excel

    前言

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

    具体实现方式

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

     1       /// <summary>
     2         /// 传入数据,返回Excel流文件
     3         /// </summary>
     4         /// <typeparam name="T"></typeparam>
     5         /// <param name="TSources">数据源</param>
     6         /// <param name="sheetName">sheet名字</param>
     7         /// <returns></returns>
     8         private async Task<MemoryStream> GetExcelMemoryStreams<T>(List<T> 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 /// <summary>
     2     /// Excel导出用
     3     /// </summary>
     4     [AttributeUsage(AttributeTargets.Property)]//标记只能用在属性上面
     5     public class ExportExcelColumnAttribute : Attribute
     6     {
     7         private string _Name = null;
     8 
     9         /// <summary>
    10         /// 构造涵数传入值 
    11         /// </summary>
    12         /// <param name="Name"></param>
    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         /// <summary>
     2         /// 获取属性设置的导出备注
     3         /// </summary>
     4         /// <param name="prop"></param>
     5         /// <returns></returns>
     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         /// <summary>
     4         /// 订单编号
     5         /// </summary> 
     6         [ExportExcelColumn("订单编号")]
     7         public string OrdernNumber { get; set; }
     8 
     9         /// <summary>
    10         /// 赠购单id
    11         /// </summary>
    12         public long? OrderID { get; set; }
    13 
    14         /// <summary>
    15         /// 赠送单号
    16         /// </summary>
    17         [ExportExcelColumn("赠送单号")]
    18         public string ComplimentaryOrderNumber { get; set; }
    19         /// <summary>
    20         /// 收费单号
    21         /// </summary>
    22         [ExportExcelColumn("收费单号")]
    23         public string AdditionalOrdernNumber { get; set; }
    24 
    25         /// <summary>
    26         /// 客户名称
    27         /// </summary>
    28         [ExportExcelColumn("客户名称")]
    29         public string CompanyName { get; set; }
    30 }       

      5),接口服务调用:

       6).控制器需要将返回的内存流转化为文件流(FileStreamResult)返回到前端响应。

       7).前端js代码

    //点击导出按钮
                $("#export_excel").click(function () {
                    var input = {
                       ....
                    };
                    
                    const req = new XMLHttpRequest();
                    req.open('Post', '你的请求地址', true);
                    req.responseType = 'blob';
                    req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                    req.onload = function () {
                        const data = req.response;
                        const blob = new Blob([data]);
                        let blobUrl = window.URL.createObjectURL(blob);
                        download(blobUrl, "你要导出的文件名");
                    };
                    var str = "";
                    var keys = Object.keys(input);
                    $.each(keys, function (idx, val) {
                        str += `&${val}=${input[val]}`;
                    });
                    console.log(str.substring(1, str.length))
                    req.send(str);
                });
    
                function download(blobUrl,fileName) {
                    const a = document.createElement('a');
                    a.style.display = 'none';
                    a.download = fileName;
                    a.href = blobUrl;
                    a.click();
                    document.body.removeChild(a);
                }    
    

      

    温馨提示

      a),上述第34行 index 作用主要是防止出现空白列,(实体列中不标识某列导出时(如上述实体 赠送订单ID),会显示空白列的问题:如下图情况:);

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

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

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

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

  • 相关阅读:
    【BZOJ 2124】【CodeVS 1283】等差子序列
    【BZOJ 1036】【ZJOI 2008】树的统计Count
    【BZOJ 1901】【ZJU 2112】Dynamic Rankings
    【BZOJ 3924】【ZJOI 2015】幻想乡战略游戏
    【BZOJ 4103】【THUSC 2015】异或运算
    【BZOJ 4513】【SDOI 2016】储能表
    【HDU 3622】Bomb Game
    【BZOJ 3166】【HEOI 2013】Alo
    【BZOJ 3530】【SDOI 2014】数数
    【BZOJ 4567】【SCOI 2016】背单词
  • 原文地址:https://www.cnblogs.com/Jinfeng1213/p/11341529.html
Copyright © 2011-2022 走看看