导出数据到Excel表格以及从Excel表格中读取数据是我们日常开发很多情况都会遇到的,这里或许只是导出一些简单的数据到excel,这里或许会按照一定的模板导出数据到excel,这里也可能需要从Excel中读取数据。为了避免重复性的劳动,这里本人结合开发中遇到情况,在基于Npoi操作Excel的情况进一步对Excel的操作进行封装,旨在更方便的开发,开箱即用,只需要短短几行代码即可实现。如果您需要使用这个包,可以在NuGet上搜索OpenDeepSpace.Npoi进行使用。
本人采用基于特性,以及基于Excel模板的方式实现了:
1.无模板数据导出到Excel
2.基于模板导出数据到Excel
3.从Excel读取数据
以下我将通过代码的形式来告诉你该如何使用他。由于以下所有的案列都是基于同一个输出对象。
首先将为大家介绍ExcelColumn特性的使用。如果某一个类的某些数据需要导出到Excel中,你就需要在类上打上ExcelColumn特性。
ExcelColumn特性有两个参数可以进行设置。
参数名 | 含义 |
---|---|
colName | 列名(输出到Excel中的列名) |
colOrder | 列顺序(该列在Excel中顺序) |
源码见github:OpenDeepSpace.Npoi
例如下面这个输出对象 本文所有导出到Excel的数据都采用该对象
/// <summary>
/// 泛指ExcelData数据输出对象
/// </summary>
public class ExcelDataOutDto
{
[ExcelColumn("唯一标识",1)]//通过ExcelColumn特性指定为需要输出到Excel的列,参数分别为列名,列排序
/// <summary>
/// Id
/// </summary>
public string Id { get; set; }
[ExcelColumn("姓名", 2)]
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
[ExcelColumn("年龄", 3)]
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
[ExcelColumn("出生日期",4)]
/// <summary>
/// 出生日期
/// </summary>
public DateTime BirthDate { get; set; }
/// <summary>
/// 其他信息
/// </summary>
public string OtherInfo { get; set; }
}
这是我定义的一些初始数据
List<ExcelDataOutDto> excelDataOutDtos = new List<ExcelDataOutDto>() {
new ExcelDataOutDto(){
Id=Guid.NewGuid().ToString(),
Name="小张",
Age=20,
BirthDate=DateTime.Today.AddYears(-20),
OtherInfo="我是小张,我不嚣张"
},
new ExcelDataOutDto(){
Id=Guid.NewGuid().ToString(),
Name="小李",
Age=24,
BirthDate=DateTime.Today.AddYears(-24),
OtherInfo="面对疾风吧"
},
new ExcelDataOutDto(){
Id=Guid.NewGuid().ToString(),
Name="小吴",
Age=27,
BirthDate=DateTime.Today.AddYears(-27),
OtherInfo="哈哈哈笑而不语"
},
};
无模板导出数据到Excel
无模板导出数据到指定路径
这里你只需简单的两行代码即可完成最简单数据导出到Excel
ExcelHandler excelHandler = new ExcelHandler();//构造一个ExcelHandler处理对象
excelHandler.exportObjectToExcel($@"D:\excelexport\{Guid.NewGuid().ToString("N")}.xlsx", excelDataOutDtos);//导出数据
导出的结果以及说明如下:
如果你不需要序号以及需要设置导出的日期格式
excelHandler.setIsSetOrder(false);//不设置序列号
excelHandler.getExcelDatePattern().setDatePattern("yyyy/MM/dd");//指定日期的格式化模式
如果需要进一步筛选需要导出的字段,即打了ExcelColumn特性的列不需要全部导出,可以加入如下代码:
//指定导出列 默认只要写了ExcelColumn特性的都会导出,这里可以对ExcelColumn进行进一步筛选
List<string> colNames = new List<string>();
colNames.Add("姓名");
colNames.Add("出生日期");
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.exportObjectToExcel($@"D:\excelexport\{Guid.NewGuid().ToString("N")}.xlsx", excelDataOutDtos,colNames);
无模板导出到流
ExcelHandler excelHandler = new ExcelHandler();
NpoiMemoryStream npoiMemoryStream=new NpoiMemoryStream();
excelHandler.exportObjectToExcel(npoiMemoryStream, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"{Guid.NewGuid()}.xlsx");
根据模板导出到Excel
Excel模板的制作需要的一些规则:
Excel模板中的占位符 | 含义 |
---|---|
datas | 填在Excel的单元格表示数据开始位置 |
styles | 填在Excel的单元格表示该列所有单元格的样式 |
defaultStyles | 填在Excel的单元格表示单元格的默认样式 |
# | 以#号开头紧接着包含英文 表示非规则的信息填充 一般用在头部或底部 例如#datetime |
order | 填在excel单元格中 指定序号占位符 可以以此来自定义序号列的名称 |
例如如下三个模板:
无头部底部模板:
有头部底部的模板:
有头部底部带序号的模板
模板不包含头部底部信息的导出
模板如下:
存在空列这里如果不设置空列数将整体先前移动,即会自动移动到A列开始
根据模板文件导出不包含头部底部信息到指定路径
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);//设置excel的列数
excelHandler.getExcelTemplate().setDataStartColNum(2);//设置数据开始列
excelHandler.exportObjectToExcelByTemplate(Path.Combine(@"ExcelTemplates/导出的数据模板无头部底部.xlsx"), $@"D:\excelexporttemplate\指定路径模板{Guid.NewGuid().ToString("N")}.xlsx", excelDataOutDtos);//Path.Combine表示模板文件是放在工程启动项目所在文件夹下面
根据模板文件导出不包含头部底部信息到流
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4 );//设置excel的列数
//导出到流文件
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream(); excelHandler.exportObjectToExcelByTemplate(Path.Combine(@"ExcelTemplates/导出的数据模板无头部底部.xlsx"), npoiMemoryStream, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"模板流文件无头部底部{Guid.NewGuid()}.xlsx");
根据模板文件流导出不包含头部底部信息到指定路径
public async Task<FileResult> ExportObjectToExcelByTemplate(IFormFile templateFormFile)
{
var streamone=templateFormFile.OpenReadStream();
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.getExcelTemplate().setDataStartColNum(2);
//导出到指定路径
excelHandler.exportObjectToExcelByTemplate(streamone, $@"D:\excelexportstreamtemplate\指定路径模板{Guid.NewGuid().ToString("N")}.xlsx", excelDataOutDtos);
}
根据模板文件流导出不包含头部底部信息到流
public async Task<FileResult> ExportObjectToExcelByTemplate(IFormFile templateFormFile)
{
var streamone=templateFormFile.OpenReadStream();
//导出到流
ExcelHandler excelHandler=new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.getExcelTemplate().setDataStartColNum(2);
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
excelHandler.exportObjectToExcelByTemplate(streamThree, npoiMemoryStream, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"模板流文件无头部底部{Guid.NewGuid()}.xlsx");
}
导出数据结果如下:
模板包含头部底部信息的导出
模板如下:
根据模板导出包含头部底部信息到指定路径
ExcelHandler excelHandler=new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4 );
Dictionary<string, object> headerAndFooterMaps = new Dictionary<string, object>();//头部底部信息 key为#号后面的部分
headerAndFooterMaps.Add("datetime", DateTime.Now);
headerAndFooterMaps.Add("exporter", "OpenDeepSpace管理员");
headerAndFooterMaps.Add("count", excelDataOutDtos.Count);
excelHandler.exportObjectToExcelByTemplate(@"C:\Users\lenovo\Desktop\导出的数据模板含头部底部.xlsx", $@"D:\excelexporthftemplate\指定路径带头部底部模板{Guid.NewGuid().ToString("N")}.xlsx",headerAndFooterMaps,excelDataOutDtos);
根据模板导出包含头部底部信息到流
ExcelHandler excelHandler = new ExcelHandler();
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.exportObjectToExcelByTemplate(@"C:\Users\lenovo\Desktop\导出的数据模板含头部底部.xlsx",npoiMemoryStream, headerAndFooterMaps, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"模板流文件有头部底部{Guid.NewGuid()}.xlsx");
}
根据模板文件流导出包含头部底部信息到指定路径
public async Task<FileResult> ExportObjectToExcelByTemplateIncludeHeaderAndFooter(IFormFile templateFormFile)
{
var streamone = templateFormFile.OpenReadStream();
Dictionary<string, object> headerAndFooterMaps = new Dictionary<string, object>();
headerAndFooterMaps.Add("datetime", DateTime.Now);
headerAndFooterMaps.Add("exporter", "OpenDeepSpace管理员");
headerAndFooterMaps.Add("count", excelDataOutDtos.Count);
excelHandler.exportObjectToExcelByTemplate(streamone, $@"D:\excelexporthftemplate\指定路径模板{Guid.NewGuid().ToString("N")}.xlsx",headerAndFooterMaps, excelDataOutDtos);
}
根据模板文件流导出包含头部底部信息到流
public async Task<FileResult> ExportObjectToExcelByTemplateIncludeHeaderAndFooter(IFormFile templateFormFile)
{
var streamone = templateFormFile.OpenReadStream();
Dictionary<string, object> headerAndFooterMaps = new Dictionary<string, object>();
headerAndFooterMaps.Add("datetime", DateTime.Now);
headerAndFooterMaps.Add("exporter", "OpenDeepSpace管理员");
headerAndFooterMaps.Add("count", excelDataOutDtos.Count);
//导出到流
excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
excelHandler.exportObjectToExcelByTemplate(streamone, npoiMemoryStream,headerAndFooterMaps, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"模板流文件有头部底部{Guid.NewGuid()}.xlsx");
}
导出结果如下:
模板已经存在列名的导出
如果模板中已经存在列名,将不会使用ExcelColumn特性中的colName作为列名
模板如下:
根据模板导出存在列名包含头部底部信息到指定路径
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.setIsClasspath(false);//指定模板是否在工程路径下
excelHandler.getExcelTemplate().setIsOrder(true);
excelHandler.getExcelTemplate().setOrderName("排序号");//给序号列设置名称
Dictionary<string, object> headerAndFooterMaps = new Dictionary<string, object>();
headerAndFooterMaps.Add("datetime", DateTime.Now);
headerAndFooterMaps.Add("exporter", "OpenDeepSpace管理员");
headerAndFooterMaps.Add("count", excelDataOutDtos.Count);
excelHandler.exportObjectToExcelByTemplateHasColName(@"C:\Users\lenovo\Desktop\导出的数据模板含头部底部有列名.xlsx", $@"D:\excelexporthftemplate\指定路径带头部底部模板{Guid.NewGuid().ToString("N")}.xlsx", headerAndFooterMaps, excelDataOutDtos);
根据模板导出存在列名包含头部底部信息到流
ExcelHandler excelHandler = new ExcelHandler();
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.getExcelTemplate().insertOrder();
excelHandler.getExcelTemplate().setOrderName("排序号");
excelHandler.exportObjectToExcelByTemplateHasColName(@"C:\Users\lenovo\Desktop\导出的数据模板含头部底部有列名.xlsx", npoiMemoryStream, headerAndFooterMaps, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"模板流文件有头部底部{Guid.NewGuid()}.xlsx");
根据模板文件流导出存在列名包含头部底部信息到指定路径
public async Task<FileResult> ExportObjectToExcelHasColNameByStream(IFormFile templateFormFile)
{
var streamone = templateFormFile.OpenReadStream();
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.setIsClasspath(false);
excelHandler.getExcelTemplate().setIsOrder(true);
excelHandler.getExcelTemplate().insertOrder();
excelHandler.getExcelTemplate().setOrderName("排序号");//给序号列设置名称
Dictionary<string, object> headerAndFooterMaps = new Dictionary<string, object>();
headerAndFooterMaps.Add("datetime", DateTime.Now);
headerAndFooterMaps.Add("exporter", "OpenDeepSpace管理员");
headerAndFooterMaps.Add("count", excelDataOutDtos.Count);
excelHandler.exportObjectToExcelByTemplateHasColName(streamone, $@"D:\excelexporthftemplate\指定路径带头部底部模板{Guid.NewGuid().ToString("N")}.xlsx", headerAndFooterMaps, excelDataOutDtos);
}
根据模板文件流导出存在列名包含头部底部信息到流
public async Task<FileResult> ExportObjectToExcelHasColNameByStream(IFormFile templateFormFile)
{
var streamone = templateFormFile.OpenReadStream();
ExcelHandler excelHandler = new ExcelHandler();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.setIsClasspath(false);
excelHandler.getExcelTemplate().setIsOrder(true);
excelHandler.getExcelTemplate().insertOrder();
excelHandler.getExcelTemplate().setOrderName("排序号");//给序号列设置名称
Dictionary<string, object> headerAndFooterMaps = new Dictionary<string, object>();
headerAndFooterMaps.Add("datetime", DateTime.Now);
headerAndFooterMaps.Add("exporter", "OpenDeepSpace管理员");
headerAndFooterMaps.Add("count", excelDataOutDtos.Count);
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
excelHandler.getExcelTemplate().setExcelColNums(4);
excelHandler.getExcelTemplate().insertOrder();
excelHandler.getExcelTemplate().setOrderName("排序号");
excelHandler.exportObjectToExcelByTemplateHasColName(streamone, npoiMemoryStream, headerAndFooterMaps, excelDataOutDtos);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"模板流文件有头部底部{Guid.NewGuid()}.xlsx");
}
导出结果如下:
从Excel导出数据到对象集合
这个是根据Excel列名对应ExcelColumn特性来进行导入。
Excel数据截图如下:
从Excel文件导出数据
ExcelHandler excelHandler = new ExcelHandler();
var excelDatas = excelHandler.exportExcelToObject<ExcelDataOutDto>(@"C:\Users\lenovo\Desktop\Excel数据导出到对象.xlsx");
从Excel流文件导出数据
public void ExportExcelToObjectByStream(IFormFile formFile)
{
ExcelHandler excelHandler = new ExcelHandler();
var excelDatas = excelHandler.exportExcelToObject<ExcelDataOutDto>(formFile.OpenReadStream());
}
导出图片到Excel中
暂未实现
多Sheet的导出
暂未实现
注意:从1.1.0版本开始ExcelHandler变为ExcelHandle,并且新增合并单元格操作以及为DateTime字段指定时间格式 1.1.2版本增加基于列的合并特性
合并相同列以及指定Datetime格式化
public class ExcelDataOutDto
{
[ExcelColumn("唯一标识",1,MergeColumn =true)]//通过ExcelColumn特性指定为需要输出到Excel的列,参数分别为列名,列排序
/// <summary>
/// Id
/// </summary>
public string Id { get; set; }
[ExcelColumn("姓名", 2,IsBaselineCol =true,MergeColumn =true)]
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
[ExcelColumn("年龄", 3,MergeColumn =true)]
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
[ExcelColumn("出生日期",4,DatePattern ="yyyy-MM-dd HHmmss")]//指定日期
/// <summary>
/// 出生日期
/// </summary>
public DateTime BirthDate { get; set; }
/// <summary>
/// 其他信息
/// </summary>
public string OtherInfo { get; set; }
}
Excel导出效果如下:
增加数据验证特性
这里如果你需要对导入或导出的数据验证,在1.1.3版本中可以通过继承DataValidationAttribute特性,实现IsValid方法来完成数据的验证.目前还没有提供一些默认的验证特性,后续考虑提供。
如下代码:
实现数据验证特性
//不为空
public class NotNullAttribute:DataValidationAttribute
{
public override DataValidationResult? IsValid(object data)
{
if (data == null || string.IsNullOrWhiteSpace(data.ToString()))
return new DataValidationResult(ErrorMessage);
return DataValidationResult.Success;
}
}
//最大长度
public class MaxLengthAttribute : DataValidationAttribute
{
public int MaxLength { get; set; }
public override DataValidationResult IsValid(object data)
{
if (data.ToString().Length > MaxLength)
return new DataValidationResult(ErrorMessage);
return DataValidationResult.Success;
}
}
使用数据验证特性:
[NotNull(ErrorMessage ="姓名不能为空")]
[MaxLength(MaxLength =100, ErrorMessage ="姓名最大长度不能超过100个字符")]
[ExcelColumn("姓名", 2,IsBaselineCol =true,MergeColumn =true)]
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
版本1.1.7中增加基于ExcelSheetAttribute特性以及基于父子级递归关联方式的多Sheet导出(仅限基于非模板方式的导出,且原则上支持无限级)
版本1.1.7增加ExcelSheetAttribute特性
ExcelSheetAttribute特性可以使用在类上和属性上,可以设置SheetName,即表示指定类输出到的Sheet的名称。
使用在类上:表示该类对应的数据输出的Sheet的名称
使用在属性上:表示该属性是一个关联的Sheet导出,建议采用List
结合ExcelSheetAttribute实现基于父子级递归关联方式的多Sheet导出
例子如下:
三级关联例子
/// <summary>
/// 泛指ExcelData数据输出对象
/// </summary>
[ExcelSheet(SheetName = "主数据")]
public class ExcelDataOutDto
{
[ExcelColumn("唯一标识", 1, MergeColumn = true)]//通过ExcelColumn特性指定为需要输出到Excel的列,参数分别为列名,列排序
/// <summary>
/// Id
/// </summary>
public string Id { get; set; }
//[NotNull(ErrorMessage = "姓名不能为空")]s
//[MaxLength(MaxLength = 100, ErrorMessage = "姓名最大长度不能超过100个字符")]
[ExcelColumn("姓名", 2, IsBaselineCol = true, MergeColumn = true)]
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
[ExcelColumn("年龄", 3, MergeColumn = true)]
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
[ExcelColumn("出生日期", 4, DatePattern = "yyyy-MM-dd HHmmss")]
/// <summary>
/// 出生日期
/// </summary>
public DateTime BirthDate { get; set; }
/// <summary>
/// 其他信息
/// </summary>
public string OtherInfo { get; set; }
/// <summary>
/// 关联数据
/// </summary>
[ExcelSheet]
public List<ExcelRelationDataOutDto> ExcelRelationDataOutDtos { get; set; }
}
[ExcelSheet(SheetName = "关联数据")]
public class ExcelRelationDataOutDto
{
[ExcelColumn(ColOrder =1,ColName ="名称",MergeColumn =true,IsBaselineCol =true)]
public string Name { get; set; }
/// <summary>
/// 关联子数据
/// </summary>
[ExcelSheet]
public List<ExcelRelationDataRelationOutDto> RelationData { get; set; }
}
/// <summary>
/// <see cref="ExcelRelationDataOutDto"/>的关联子数据
/// </summary>
[ExcelSheet(SheetName = "孙子数据")]
public class ExcelRelationDataRelationOutDto
{
[ExcelColumn(ColOrder = 1, ColName = "名称", MergeColumn = true, IsBaselineCol = true)]
public string Name { get; set; }
}
三级关联的数据
List<ExcelDataOutDto> excelDataOutDtos = new List<ExcelDataOutDto>() {
new ExcelDataOutDto(){
Id=Guid.NewGuid().ToString(),
Name="小张",
Age=20,
BirthDate=DateTime.Today.AddYears(-20),
OtherInfo="我是小张,我不嚣张",
ExcelRelationDataOutDtos=new List<ExcelRelationDataOutDto>()
{
new ExcelRelationDataOutDto(){ Name="小张关联"},
new ExcelRelationDataOutDto(){ Name="小张关联"},
new ExcelRelationDataOutDto(){
Name="小张关联3",
//子数据的子数据
RelationData =new List<ExcelRelationDataRelationOutDto>()
{
new ExcelRelationDataRelationOutDto(){ Name="小张孙子数据"}
}
},
}
},
new ExcelDataOutDto(){
Id="b66f90b7-178b-11ed-98e5-00155d562808",
Name="小李",
Age=24,
BirthDate=DateTime.Today.AddYears(-24),
OtherInfo="面对疾风吧"
},
new ExcelDataOutDto(){
Id="b66f90b7-178b-11ed-98e5-00155d562808",
Name="小李",
Age=24,
BirthDate=DateTime.Today.AddYears(-24),
OtherInfo="面对疾风吧八八八",
ExcelRelationDataOutDtos=new List<ExcelRelationDataOutDto>()
{
new ExcelRelationDataOutDto(){
Name="小李关联",
//子数据的子数据
RelationData =new List<ExcelRelationDataRelationOutDto>()
{
new ExcelRelationDataRelationOutDto(){ Name="小李孙子数据"},
new ExcelRelationDataRelationOutDto(){ Name="小李孙子数据"},
new ExcelRelationDataRelationOutDto(){ Name="小李孙子数据2"}
}
}
}
},
/*new ExcelDataOutDto(){
Id=Guid.NewGuid().ToString(),
Name="小红",
Age=24,
BirthDate=DateTime.Today.AddYears(-24),
OtherInfo="面对疾风吧八八八"
},*/
new ExcelDataOutDto(){
Id="b66f90b7-178b-11ed-98e5-00155d5s2808",
Name="小吴",
Age=27,
BirthDate=DateTime.Today.AddYears(-27),
OtherInfo="哈哈哈笑而不语",
ExcelRelationDataOutDtos=new List<ExcelRelationDataOutDto>()
{
new ExcelRelationDataOutDto(){ Name="小吴关联"},
new ExcelRelationDataOutDto(){ Name="小吴关联3"}
}
},
new ExcelDataOutDto(){
Id="b66f90b7-178b-11ed-98e5-00155d5s2808",
Name="小吴",
Age=27,
BirthDate=DateTime.Today.AddYears(-27),
OtherInfo="哈哈哈笑笑笑"
},
new ExcelDataOutDto(){
Id="b66f90b7-178b-11ed-98e5-00155d5s2809",
Name="",
Age=27,
BirthDate=DateTime.Today.AddYears(-27),
OtherInfo="哈哈哈笑笑笑"
},
};
导出例子:
/// <summary>
/// 无模板导出数据到excel并返回流文件供下载
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task<FileResult> ExportObjectToExcelToStream()
{
ExcelHandle ExcelHandle = new ExcelHandle();
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
List<ExcelDataOutDto> excelDataOutDtosOver = new List<ExcelDataOutDto>();
for (int i = 0; i < 10000; i++)
{
excelDataOutDtosOver.AddRange(excelDataOutDtos);
}
ExcelHandle.exportObjectToExcel(npoiMemoryStream, excelDataOutDtosOver);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"{Guid.NewGuid()}.xlsx");
}
导出结果如下:
版本1.1.8
修复部分错误。数据验证错误信息ErrorMessage中增加 {row} 占位符,来实现行下标的错误数据的提示。
比如:
/// <summary>
/// 部门
/// </summary>
[ColumnNotNull(ErrorMessage = "第{row}行数据:部门不能为空")]
[ExcelColumn(ColOrder = 1, ColName = "部门")]
public string Department { get; set; }
测试结果如下如果部门列下对应的数据为空,就会抛出如下异常:
当然如果你想自己处理异常,抛出你自己定义的友好异常可以try catch捕获这个异常然后处理。
版本1.1.9 异常处理优化
这里我分为两种异常,一种是数据异常,一种是列名表头异常。
/// <summary>
/// Npoi异常码
/// </summary>
public class NpoiExceptionCode
{
/// <summary>
/// 数据异常
/// </summary>
public const int DataException = 1000;
/// <summary>
/// 列名异常
/// </summary>
public const int ColumnNameException = 2000;
}
数据异常就是:数据验证的时候出现异常,通过NpoiException包裹
列名异常就是:读取Excel中的数据时,如果对应列名查找不到那么就会通过NpoiException包裹 ColumnNameException。
你可以通过try catch捕获它,然后来实现用自己的异常抛出处理。比如这样:
//读取文件中数据
ExcelHandle excelHandle = new ExcelHandle();
List<EmergencyWorkerInDto> emergencyWorkerInDtos = new List<EmergencyWorkerInDto>();
using (var stream = file.OpenReadStream())
{
try
{
var fileContent = excelHandle.exportExcelToObject<EmergencyWorkerInDto>(stream,IsAutoSkipNullRow:false);
if (fileContent.Count == 0)
throw new FriendlyException("Excel文件中没有任何数据");
foreach (var item in fileContent)
{
item.ResponseLevelId = ResponseLevelId;
emergencyWorkerInDtos.Add(item);
}
}
catch (Exception ex)
{
if (ex is NpoiException npoiException)
{
if (npoiException.ExceptionCode == NpoiExceptionCode.ColumnNameException)
throw new FriendlyException("Excel文件与模板不符合");
else
throw new FriendlyException(npoiException.Message);
}
else
{
throw new FriendlyException(ex.Message);
}
}
}
注意对于文件中除了表头没有数据有两种:第一种是除了表头,其他没有单元格没有任何填充(包括单元格背景设置),这种是需要自己判断处理,如上throw new FriendlyException(“Excel文件中没有任何数据”);
还有一种就是单元格有填充但无数据,这种就不是空,如果设置了数据验证就会抛出异常。
版本1.1.11 基于Json格式的数据导出到Excel,支持多sheet
你需要按照格式生成一个如下的JsonArray的数据:
JsonArray里面的每一个JsonObject,则对应输出到一个Sheet,所以这里可以输出多个Sheet
其中ColumnTitles指明列名,可选项,也可不传,不传以后面Datas数据中属性名称为列名 例如"Name":“小李” 这里以Name为列名
其中SheetName指明Sheet表的名称,可选项,
其中Datas指明数据,必须项,为一个JsonArray,里面是所有数据
其中IsOrder指定是否输出序号,true表示输入,可选项,默认为true
[{"ColumnTitles":["姓名","部门"],"SheetName":"第一个sheet名称","Datas":[{"Name":"小李","Department":"管理部"}],"IsOrder":true},{}]
简单的只有数据
[{"Datas":[{"Name":"小李","Department":"管理部"}]},{}]
//使用方式如下
/// <summary>
///导出json数据到excel并返回流文件供下载
/// </summary>
/// <returns></returns>
[HttpPost]
public async Task<FileResult> ExportJsonToExcel(string JsonData)
{
NpoiMemoryStream npoiMemoryStream = new NpoiMemoryStream();
ExcelHandle excelHandle=new ExcelHandle();
excelHandle.exportJsonToExcel(npoiMemoryStream,JsonData);
npoiMemoryStream.Seek(0, SeekOrigin.Begin);
return File(npoiMemoryStream, "application/stream", $"{Guid.NewGuid()}.xlsx");
}
//结果