根据数据源自定义字段实现动态导出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会自动回收;
根据数据源自定义字段实现动态导出Excel的更多相关文章
- JavaWeb动态导出Excel可弹出下载
由于项目需求,需要将数据导出成Excel表格,并且可选择导出项,可下载.项目使用的Spring+Mybatis+SpringMVC框架,利用Apache POI导出Excel.POI具体使用请自行百度 ...
- Java 导出EXCEL
1.Apache POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程式对Microsoft Office格式档案读和写的功能. .NET的开发人员则 ...
- POI导出EXCEL经典实现
1.Apache POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程式对Microsoft Office格式档案读和写的功能. .NET的开发人员则 ...
- struts2中利用POI导出Excel文档并下载
1.项目组负责人让我实现这个接口,因为以前做过类似的,中间并没有遇到什么太困难的事情.其他不说,先上代码: package com.tydic.eshop.action.feedback; impor ...
- Java POI 导出EXCEL经典实现 Java导出Excel
转自http://blog.csdn.net/evangel_z/article/details/7332535 在web开发中,有一个经典的功能,就是数据的导入导出.特别是数据的导出,在生产管理或者 ...
- java中使用poi导出excel表格数据并且可以手动修改导出路径
在我们开发项目中,很多时候会提出这样的需求:将前端的某某数据以excel表格导出,今天就给大家写一个简单的模板. 这里我们选择使用poi导出excel: 第一步:导入需要的jar包到 lib 文件夹下
- EasyUI 如何结合JS导出Excel文件
出处:http://blog.csdn.net/jumtre/article/details/41119991 EasyUI 如何结合JS导出Excel文件 分类: 技术 Javascript jQu ...
- js导出excel:前端当前数据的导出
网上找的库文件,同样做了修改.在导出的时候,有时候数据第一列和最后一列可能是复选框和操作按钮,这个是我们不需要的,加了这个的过滤 //table2excel.js /* * jQuery table2 ...
- POI导出EXCEL经典实现(转)
http://www.cnblogs.com/xwdreamer/archive/2011/07/20/2296975.html 1.Apache POI简介 Apache POI是Apache软件基 ...
随机推荐
- spring boot +dubbo 踩坑记录
今天初次搭建spring boot +duboo的demo.记录一下踩坑记录. 首先搭建3个小demo,一个maven项目,两个spring boot (服务提供者和服务消费者)项目. 两 sprin ...
- StrictMode 检测应用
Application, Activity, or other application component's onCreate() method:if (BuildConfig.SHOW_LOG) ...
- iPhone去除input默认样式
/*<!---->去掉苹果短的样式*/ input[type="button"], input[type="submit"], input[type ...
- C#中委托、匿名函数、Lambda表达式的一些个人理解
0x01定义一个委托,相当于定义一个可以存储方法的特殊变量类型 下面我们看具体的代码,通过代码更好理解 delegate void IntMethodInvoker(int x); 这行代码就是声明一 ...
- 基于Linux的tty架构及UART驱动详解
更多嵌入式Linux原创,请关注公众号:一口Linux 一.模块硬件学习 1.1. Uart介绍 通用异步收发传输器(Universal Asynchronous Receiver/Transmitt ...
- WPF 基础 - 控件与布局
1. 前言 1. 数据驱动 UI WPF 是数据核心.主动的,UI 从属数据并表达数据.是被动的: 不再是 UI 驱动数据,而是数据驱动 UI: 2. 控件的定义 控件.数据内容.行为(控件响应用户操 ...
- Python装饰器(2)
上一篇介绍了装饰器的常规使用方法,即函数形式的装饰器.这篇文章中,进一步介绍类class跟装饰器的相关知识. [用装饰器来装饰类函数] 这是今天介绍的第一种使用场景,比较常见.因为目前好多的编程语言都 ...
- Gevent高并发网络库精解
进程 线程 协程 异步 并发编程(不是并行)目前有四种方式:多进程.多线程.协程和异步. 多进程编程在python中有类似C的os.fork,更高层封装的有multiprocessing标准库 多线程 ...
- python ORM之sqlalchemy
前沿对象关系映射ORM是在实际应用编程中常用到的技术,它在对象和关系之间建立了一条桥梁,前台的对象型数据和数据库中的关系型的数据通过这个桥梁来相互转化.简单来说就是开发人员在使用ORM模型编程时,不需 ...
- python之routes入门
一.入门 from routes import Mapper map = Mapper() # 创建一个mapper()路由实例对象 # connect注册路由信息 # 路由名称'zbj', 路径是 ...