分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续3篇-导出时动态生成多Sheet EXCEL)
ExcelUtility 类库经过我(梦在旅途)近期不断的优化与新增功能,现已基本趋向稳定,功能上也基本可以满足绝大部份的EXCEL导出需求,该类库已在我们公司大型ERP系统全面使用,效果不错,今天应用户的特殊需求,我又新增了一个功能,导出时动态生成多Sheet EXCEL。
新增方法一:由GetFormatterContainer Func委托导出基于EXCEL模板的多Sheet文件,方法定义如下:
/// <summary>
/// 由GetFormatterContainer Func委托导出基于EXCEL模板的多工作薄文件
/// </summary>
/// <typeparam name="T">数据源可枚举项类型</typeparam>
/// <param name="templatePath">模板路径</param>
/// <param name="sheetName">模板中使用的工作薄名称</param>
/// <param name="dataSource">数据源</param>
/// <param name="getFormatterContainer">生成模板数据格式化容器(SheetFormatterContainer)委托,在委托方法中实现模板的格式化过程</param>
/// <param name="sheetSize">每个工作薄显示的数据记录数</param>
/// <param name="filePath">导出路径,可选</param>
/// <returns></returns>
public static string ToExcelWithTemplate<T>(string templatePath, string sheetName, IEnumerable<T> dataSource, Func<IEnumerable<T>, SheetFormatterContainer> getFormatterContainer, int sheetSize, string filePath = null)
{ if (!File.Exists(templatePath))
{
throw new FileNotFoundException(templatePath + "文件不存在!");
} bool isCompatible = Common.GetIsCompatible(templatePath); if (string.IsNullOrEmpty(filePath))
{
filePath = Common.GetSaveFilePath(isCompatible);
}
else if (isCompatible && !Path.GetExtension(filePath).Equals(".xls", StringComparison.OrdinalIgnoreCase))
{
throw new ArgumentException("当模板采用兼容模式时(低版本格式,如:xls,xlt),则指定的导出文件格式必需为xls。");
} if (string.IsNullOrEmpty(filePath)) return null; int sheetCount = 0;
var formatterContainers = new Dictionary<string, SheetFormatterContainer>();
IEnumerable<T> data = null;
while ((data = dataSource.Take(sheetSize)).Count() > 0)
{
var sheetFormatterContainer = getFormatterContainer(data);
sheetCount++;
if (sheetCount == 1)
{
formatterContainers.Add(sheetName, sheetFormatterContainer);
}
else
{
formatterContainers.Add(sheetName + sheetCount.ToString(), sheetFormatterContainer);
}
dataSource = dataSource.Skip(sheetSize);
}
string temp_templatePath = null;
try
{
temp_templatePath = Common.CreateTempFileByTemplate(templatePath, sheetName, sheetCount);
filePath = ToExcelWithTemplate(temp_templatePath, formatterContainers, filePath);
}
finally
{
if (!string.IsNullOrEmpty(temp_templatePath) && File.Exists(temp_templatePath))
{
File.Delete(temp_templatePath);
}
string temp_templateConfigFilePath = Path.ChangeExtension(temp_templatePath, ".xml");
if (File.Exists(temp_templateConfigFilePath))
{
File.Delete(temp_templateConfigFilePath);
}
} return filePath;
}
简要说明上述方法实现原理步骤:
1.指定模板路径、初始工作薄名称、导出的数据源、每个工作薄显示的记录数、封装生成模板数据格式化容器(SheetFormatterContainer)委托,在委托方法中实现模板的格式化过程;
2.依据每个工作薄显示的记录数,循环拆分数据源,并计算出需要的工作薄总数以及生成模板数据格式化容器字典(Key:Sheet名称,Value:模板数据格式化容器对象);
3.生成2中计算的所需的工作薄的临时模板文件(存放在系统的本地临时目录:Temp)
4.调用ToExcelWithTemplate的其它重载方法(基于模板+多sheet生成EXCEL方法)来完成EXCEL的导出;
5.无论最终导出成功与否,将删除临时模板及临时模板配置文件;
测试示例代码如下:
/// <summary>
/// 测试方法:测试依据模板+DataTable来生成多工作薄的EXCEL
/// </summary>
[TestMethod]
public void TestExportToExcelWithTemplateByDataTable2()
{
DataTable dt = GetDataTable();
string templateFilePath = AppDomain.CurrentDomain.BaseDirectory + "/excel.xls"; //获得EXCEL模板路径 string excelPath = ExcelUtility.Export.ToExcelWithTemplate<DataRow>(templateFilePath, "table", dt.Select(), (data) =>
{ SheetFormatterContainer formatterContainers = new SheetFormatterContainer(); //实例化一个模板数据格式化容器 PartFormatterBuilder partFormatterBuilder = new PartFormatterBuilder();//实例化一个局部元素格式化器
partFormatterBuilder.AddFormatter("Title", "跨越IT学员");//将模板表格中Title的值设置为跨越IT学员
formatterContainers.AppendFormatterBuilder(partFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 CellFormatterBuilder cellFormatterBuilder = new CellFormatterBuilder();//实例化一个单元格格式化器
cellFormatterBuilder.AddFormatter("rptdate", DateTime.Today.ToString("yyyy-MM-dd HH:mm"));//将模板表格中rptdate的值设置为当前日期
formatterContainers.AppendFormatterBuilder(cellFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效 //实例化一个表格格式化器,data是已拆分后的数据源(这里是10条记录),name表示的模板表格中第一行第一个单元格要填充的数据参数名
TableFormatterBuilder<DataRow> tableFormatterBuilder = new TableFormatterBuilder<DataRow>(data, "name");//这里的数据源设置:data是重点
tableFormatterBuilder.AddFormatters(new Dictionary<string, Func<DataRow, object>>{
{"name",r=>r["Col1"]},//将模板表格中name对应DataTable中的列Col1
{"sex",r=>r["Col2"]},//将模板表格中sex对应DataTable中的列Col2
{"km",r=>r["Col3"]},//将模板表格中km对应DataTable中的列Col3
{"score",r=>r["Col4"]},//将模板表格中score对应DataTable中的列Col4
{"result",r=>r["Col5"]}//将模板表格中result对应DataTable中的列Co5
});
formatterContainers.AppendFormatterBuilder(tableFormatterBuilder);//添加到工作薄格式容器中,注意只有添加进去了才会生效
return formatterContainers;//返回一个模板数据格式化容器 }, 10);//注意这里的10表示动态生成新的工作薄,且每个工作薄显示10条记录 Assert.IsTrue(File.Exists(excelPath));
}
测试结果如下:
1.生成的临时模板及模板配置文件:

2.导出的EXCE结果如下:

3.临时模板及模板配置文件已被清除。
新增方法二:增加由DataTable导出多Sheet Excel方法(准确的说是修改ToExcel方法,增加一个sheetSize参数),方法定义如下:
/// <summary>
/// 由DataTable导出Excel
/// </summary>
/// <param name="sourceTable">要导出数据的DataTable</param>
/// <param name="sheetName">工作薄名称,可选</param>
/// <param name="filePath">导出路径,可选</param>
/// <param name="colNames">需要导出的列名,可选</param>
/// <param name="colAliasNames">导出的列名重命名,可选</param>
/// <param name="colDataFormats">列格式化集合,可选</param>
/// <param name="sheetSize">指定每个工作薄显示的记录数,可选(不指定或指定小于0,则表示只生成一个工作薄)</param>
/// <returns></returns>
public static string ToExcel(DataTable sourceTable, string sheetName = "result", string filePath = null, string[] colNames = null, IDictionary<string, string> colAliasNames = null, IDictionary<string, string> colDataFormats = null, int sheetSize = 0)
{
if (sourceTable.Rows.Count <= 0) return null; if (string.IsNullOrEmpty(filePath))
{
filePath = Common.GetSaveFilePath();
} if (string.IsNullOrEmpty(filePath)) return null; bool isCompatible = Common.GetIsCompatible(filePath); IWorkbook workbook = Common.CreateWorkbook(isCompatible);
ICellStyle headerCellStyle = Common.GetCellStyle(workbook, true);
//ICellStyle cellStyle = Common.GetCellStyle(workbook); if (colNames == null || colNames.Length <= 0)
{
colNames = sourceTable.Columns.Cast<DataColumn>().OrderBy(c => c.Ordinal).Select(c => c.ColumnName).ToArray();
} IEnumerable<DataRow> batchDataRows, dataRows = sourceTable.Rows.Cast<DataRow>();
int sheetCount = 0;
if (sheetSize <= 0)
{
sheetSize = sourceTable.Rows.Count;
}
while ((batchDataRows = dataRows.Take(sheetSize)).Count() > 0)
{ Dictionary<int, ICellStyle> colStyles = new Dictionary<int, ICellStyle>(); ISheet sheet = workbook.CreateSheet(sheetName + (++sheetCount).ToString());
IRow headerRow = sheet.CreateRow(0); // handling header.
for (int i = 0; i < colNames.Length; i++)
{
ICell headerCell = headerRow.CreateCell(i);
if (colAliasNames != null && colAliasNames.ContainsKey(colNames[i]))
{
headerCell.SetCellValue(colAliasNames[colNames[i]]);
}
else
{
headerCell.SetCellValue(colNames[i]);
}
headerCell.CellStyle = headerCellStyle;
sheet.AutoSizeColumn(headerCell.ColumnIndex);
if (colDataFormats != null && colDataFormats.ContainsKey(colNames[i]))
{
colStyles[headerCell.ColumnIndex] = Common.GetCellStyleWithDataFormat(workbook, colDataFormats[colNames[i]]);
}
else
{
colStyles[headerCell.ColumnIndex] = Common.GetCellStyle(workbook);
}
} // handling value.
int rowIndex = 1; foreach (DataRow row in batchDataRows)
{
IRow dataRow = sheet.CreateRow(rowIndex); for (int i = 0; i < colNames.Length; i++)
{
ICell cell = dataRow.CreateCell(i);
//cell.SetCellValue((row[colNames[i]] ?? "").ToString());
//cell.CellStyle = cellStyle;
Common.SetCellValue(cell, (row[colNames[i]] ?? "").ToString(), sourceTable.Columns[colNames[i]].DataType, colStyles);
Common.ReSizeColumnWidth(sheet, cell);
} rowIndex++;
}
sheet.ForceFormulaRecalculation = true; dataRows = dataRows.Skip(sheetSize);
} FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
workbook.Write(fs);
fs.Dispose();
workbook = null; return filePath;
}
修改代码部份说明:增加依据指定的每个工作薄显示的记录数(sheetSize)来循环拆分数据源及创建多个工作薄;
测试示例代码如下:
/// <summary>
/// 测试方法:测试将DataTable导出到多工作薄EXCEL
/// </summary>
[TestMethod]
public void TestExportToExcelByDataTable8()
{
DataTable dt = GetDataTable();
string excelPath = ExcelUtility.Export.ToExcel(dt, "sheet", sheetSize: 10);//指定每个工作薄显示的记录数 Assert.IsTrue(File.Exists(excelPath));
}
导出的EXCE结果如下:

源代码同步更新至开源社区的GIT目录中,具体地址请看我该系列之前的文章有列出,在此就不再说明。
分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility 其它相关文章链接:
分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续2篇-模板导出综合示例)
分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续3篇-导出时动态生成多Sheet EXCEL)的更多相关文章
- 分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续2篇-模板导出综合示例)
自ExcelUtility类推出以来,经过项目中的实际使用与不断完善,现在又做了许多的优化并增加了许多的功能,本篇不再讲述原理,直接贴出示例代码以及相关的模板.结果图,以便大家快速掌握,另外这些示例说 ...
- 分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续篇)
上周六我发表的文章<分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility>受到了大家的热烈支持与推荐,再此表示感谢,该ExcelUtility ...
- 分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility
1. ExcelUtility功能: 1.将数据导出到EXCEL(支持XLS,XLSX,支持多种类型模板,支持列宽自适应) 类名:ExcelUtility. Export 2.将EXCEL ...
- 【EXCEL终极总结分享】基于NPOI扩展封装的简易操作工具类库(简单灵活易用,支持导出、导入、上传等常见操作)
对于EXCEL的导入.导出,我之前已分享过多次,比如: 第一种方案:<我写的一个ExcelHelper通用类,可用于读取或生成数据>这个主要是利用把EXCEL当成一个DB来进行获取数据,导 ...
- 项目一:第四天 1、快递员的条件分页查询-noSession,条件查询 2、快递员删除(逻辑删除) 3、基于Apache POI实现批量导入区域数据 a)Jquery OCUpload上传文件插件使用 b)Apache POI读取excel文件数据
1. 快递员的条件分页查询-noSession,条件查询 2. 快递员删除(逻辑删除) 3. 基于Apache POI实现批量导入区域数据 a) Jquery OCUpload上传文件插件使用 b) ...
- NPOI+ExcelReport
分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续2篇-模板导出综合示例) 自ExcelUtility类推出以来,经过项目中的实际使用与不断完 ...
- 基于NPOI导出和导入Excel
概述 NPOI,顾名思义,就是POI的.NET版本.NPOI就是用.NET语言编写的一套数据导出Excel的开源项目,支持XML.xls.xlsx.ppt等格式..NET不仅实现Excel导出还可以实 ...
- 基于NPOI的报表引擎——ExcelReport
前言 其实现在说ExcelReport是报表引擎还为时尚早,但该组件我既然要决心维护下去,这便算是初衷吧! 1.现在,ExcelReport能为你做什么呢? 如果,你有导出数据到Excel的需求,Ex ...
- 基于NPOI导出Excel
在上一篇文章[关于大数据的查询与导出]中,提到了使用NPOI组件导出Excel,本想上次一起分享给大家,无奈最近比较忙,今天抽空整理了下,分享出来. 预置填充模板,并且需要支持公式计算; 可导入图片; ...
随机推荐
- JQuery 获取父级元素、同级元素、子元素等
例: <div> <div id="div_1">这是内容1</div> <div id="div_2">这是内 ...
- JAVA第五次作业
import javax.media.; import java.awt.; import java.awt.event.; class MediaPlayer extends Frame imple ...
- Expert 诊断优化系列-------------针对重点语句调索引
上一篇我们说了索引的重要性,一个索引不仅能让一条语句起飞,也能大量减少系统对CPU.内存.磁盘的依赖.我想上一篇中的例子可以说明了.给出上一篇和目录文链接: SQL SERVER全面优化------- ...
- 如何在没有域的环境中搭建AlwaysOn(一)
对DBA而言,不需要域就可以搭建SQL Server AlwaysOn是Windows Server 2016中最令人兴奋的功能了,它不仅可以降低搭建的成本,而且还减少了部署和运维的工作量. 该特性可 ...
- Drupal8重命名上传的中文名文件
完整的模块代码文件在Coding.net上,想直接使用的请前往下载:https://coding.net/u/yamus/p/chinese_rename/git/tree/master 最近吧Dru ...
- iOS--知识综合应用成就时髦小功能点
iOS--知识综合应用成就时髦小功能点
- iOS 数据库的增删改查(OC版)
自己写了几个方法来实现数据的增删改查功能: 首先在TARGETS--->>Build phases里面添加数据库所关联的库文件libsqlite3.tbd 添加完以后,在控制器上添加 #i ...
- OleDb Source component 用法
OleDb Source component 主要是从DB中获取数据,传递给下游组件,OleDb Source component的强大之处在于 query data 的mode有四种,如图 Tabl ...
- 【SQL】SQL Server登录常见问题
很多人SQL装的很不规范,导致各种问题或者说时间长了,密码给忘记了,怎么办呢?重装? 逆天就拿几个比较常见的来说事吧,首先是各种设置的问题:(我家里用的是简易版,你们开发最好用企业版) ...
- SQL Server 2014新特性探秘(1)-内存数据库
简介 SQL Server 2014提供了众多激动人心的新功能,但其中我想最让人期待的特性之一就要算内存数据库了.去年我再西雅图参加SQL PASS Summit 2012的开幕式时,微软就宣布 ...