分享我基于NPOI+ExcelReport实现的导入与导出EXCEL类库:ExcelUtility (续3篇-导出时动态生成多Sheet EXCEL)

简介:

ExcelUtility 类库经过我(梦在旅途)近期不断的优化与新增功能,现已基本趋向稳定,功能上也基本可以满足绝大部份的EXCEL导出需求,该类库已在我们公司大型ERP系统全面使用,效果不错,今天应用户的特殊需求,我又新增了一个功能,导出时动态生成多Sheet EXCEL。

 

新增方法一:由GetFormatterContainer Func委托导出基于EXCEL模板的多Sheet文件,方法定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/// <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.无论最终导出成功与否,将删除临时模板及临时模板配置文件;

测试示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/// <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参数),方法定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/// <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)来循环拆分数据源及创建多个工作薄;

测试示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
/// <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篇-模板导出综合示例)

本文转自 梦在旅途 博客园博客,原文链接:http://www.cnblogs.com/zuowj/p/5175726.html  ,如需转载请自行联系原作者

相关文章
|
7天前
|
文字识别 BI
【图片型PDF】批量识别扫描件PDF指定区域局部位置内容,将识别内容导出Excel表格或批量改名文件,基于阿里云OCR对图片型PDF识别改名案例实现
在医疗和政务等领域,图片型PDF文件(如病历、报告、公文扫描件)的处理需求广泛。通过OCR技术识别这些文件中的文字信息,提取关键内容并保存为表格,极大提高了信息管理和利用效率。本文介绍一款工具——咕嘎批量OCR系统,帮助用户快速处理图片型PDF文件,支持区域识别、内容提取、导出表格及批量改名等功能。下载工具后,按步骤选择处理模式、进行区域采样、批量处理文件,几分钟内即可高效完成数百个文件的处理。
49 8
|
3月前
|
前端开发
实现Excel文件和其他文件导出为压缩包,并导入
实现Excel文件和其他文件导出为压缩包,并导入
50 1
|
3月前
|
数据格式 UED
记录一次NPOI库导出Excel遇到的小问题解决方案
【11月更文挑战第16天】本文记录了使用 NPOI 库导出 Excel 过程中遇到的三个主要问题及其解决方案:单元格数据格式错误、日期格式不正确以及合并单元格边框缺失。通过自定义单元格样式、设置数据格式和手动添加边框,有效解决了这些问题,提升了导出文件的质量和用户体验。
328 3
|
3月前
|
Java API Apache
|
2月前
|
存储 Java easyexcel
招行面试:100万级别数据的Excel,如何秒级导入到数据库?
本文由40岁老架构师尼恩撰写,分享了应对招商银行Java后端面试绝命12题的经验。文章详细介绍了如何通过系统化准备,在面试中展示强大的技术实力。针对百万级数据的Excel导入难题,尼恩推荐使用阿里巴巴开源的EasyExcel框架,并结合高性能分片读取、Disruptor队列缓冲和高并发批量写入的架构方案,实现高效的数据处理。此外,文章还提供了完整的代码示例和配置说明,帮助读者快速掌握相关技能。建议读者参考《尼恩Java面试宝典PDF》进行系统化刷题,提升面试竞争力。关注公众号【技术自由圈】可获取更多技术资源和指导。
|
2月前
|
数据采集 数据可视化 数据挖掘
利用Python自动化处理Excel数据:从基础到进阶####
本文旨在为读者提供一个全面的指南,通过Python编程语言实现Excel数据的自动化处理。无论你是初学者还是有经验的开发者,本文都将帮助你掌握Pandas和openpyxl这两个强大的库,从而提升数据处理的效率和准确性。我们将从环境设置开始,逐步深入到数据读取、清洗、分析和可视化等各个环节,最终实现一个实际的自动化项目案例。 ####
305 10
|
4月前
|
数据处理 Python
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
这篇文章介绍了如何使用Python读取Excel文件中的数据,处理后将其保存为txt、xlsx和csv格式的文件。
268 3
Python实用记录(十):获取excel数据并通过列表的形式保存为txt文档、xlsx文档、csv文档
|
4月前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
4月前
|
数据采集 存储 JavaScript
自动化数据处理:使用Selenium与Excel打造的数据爬取管道
本文介绍了一种使用Selenium和Excel结合代理IP技术从WIPO品牌数据库(branddb.wipo.int)自动化爬取专利信息的方法。通过Selenium模拟用户操作,处理JavaScript动态加载页面,利用代理IP避免IP封禁,确保数据爬取稳定性和隐私性。爬取的数据将存储在Excel中,便于后续分析。此外,文章还详细介绍了Selenium的基本设置、代理IP配置及使用技巧,并探讨了未来可能采用的更多防反爬策略,以提升爬虫效率和稳定性。
260 4
|
5月前
|
数据采集 存储 数据挖掘
使用Python读取Excel数据
本文介绍了如何使用Python的`pandas`库读取和操作Excel文件。首先,需要安装`pandas`和`openpyxl`库。接着,通过`read_excel`函数读取Excel数据,并展示了读取特定工作表、查看数据以及计算平均值等操作。此外,还介绍了选择特定列、筛选数据和数据清洗等常用操作。`pandas`是一个强大且易用的工具,适用于日常数据处理工作。

热门文章

最新文章