导航菜单
首页 » 无极3登录 » 正文

平凡之路-.NET导出Excel的四种办法及评测

前语

导出Excel是.NET的常见需求,开源社区、市场上,都供给了不少林林总总的Excel操作相关包。本文,我将运用NPOI、EPPlus、OpenXML、Aspose.Cells四个市面上常见的库,各完结一个导出Excel示例。然后对其代码风格和功用做一个横向比较。终究我将说出我自己的感触。

文中一切的示例代码能够在这儿下载:

https://github.com/sdcb/blog-data/tree/master/2019/20190824-dotnet-excel-compare

NPOI

NPOI源自于Java的Apache POI(https://poi.apache.org/),现在最新版别是2.4.1。NPOI是开源项目,作者是华人(https://github.com/tonyqus/),项目地址是:https://github.com/tonyqus/npoi。

几年前咱们导出Excel都运用COM,但COM不方便,这个组件的推出无疑弥补了.NET在Excel方面组件的空白,咱们都说比COM好用。

NPOI还加入了.NET Core Community安排,项目地址是:https://github.com/dotnetcore/NPOI。

EPPlus

EPPlus是另一个开源的Excel操作库,现在最新版别是4.5.3.2。Github地址是:https://github.com/JanKallman/EPPlus。

EPPlus仅依靠根底类库BCL,彻底没有第三方包依靠,也是.NET原生库。

EPPlus只支撑导出Office 2007之后的格局,也便是xlsx。这现已是存在12年的格局了,但假如有客户想要导出xls,EPPlus将不支撑。

OpenXML

OpenXML的NuGet包全称是DocumentFormat.OpenXml:是微软推出的较为低层的Excel操作库,最新安稳版别是2.9.1。OpenXML也是开源项目,地址是:https://github.com/OfficeDev/Open-XML-SDK。

从该项意图姓名能够看出,OpenXML比较触平凡之路-.NET导出Excel的四种办法及评测及底层,因而很简略令人思绪万千,感觉它的功用、速度很或许是最快的,但真的如此吗?

Aspose.Cells

这是Aspose Pty Ltd公司推出的Excel操作库。它是很多Aspose File Format API产品其间之一。现在最新版别是19.8.0(根据年/月)。Aspose供给了包罗万象的文件格局支撑,除了.NET外,Aspose还供给了C++和Java的包。

据我所知Aspose的客户支撑服务也不错,客户提出的问题常常能够鄙人一次发布时处理。

Aspose.Cells是不开源,付费的库,但供给无限期的试用,据[官方网站](https://docs.aspose.com/display/cellsnet/Licensing#Licensing-EvaluationVersionLimitations)显现,试用版将

约束翻开文件数量100个

约束运用Aspose.Cells.GridWeb功用

生成的Excel将添加如下水印:

但经过我的试用,无论是并行仍是串行,都没找到约束翻开文件数量100个的约束。因而,“试用版”对咱们的物理约束,就只有这个水印了(当然加了这个水印,客户必定也不会有好表情)。

Excel-COM

COM是跟着Excel装置而自带的库,Excel的包名叫Microsoft.Office.Interop.Excel。本文不会深化解析,详细能够看[这篇文档](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interop/how-to-access-office-onterop-objects)。

我想要多说两句的是,COM的old-fashion(过期)不是没有原因的,据我所知COM有以下缺陷:

  • 调用时会发动一个进程外的excel.exe,或许由于它为是专门为Office规划的(不是为.NET集成规划的)

  • 要求方针环境装置相关软件,没装置将无法运转

  • 明显也没办法跨渠道

  • 运用了很多动态/多参数接口,对开发不是很友爱

  • 不像保管内存,COM对资源开释也有要求,详细拜见[这篇文章](https://www.breezetree.com/blog/common-mistakes-programming-excel-with-c-sharp)

横向比较


NPOI
EPPlus
OpenXML
Aspose
包依靠
有1个

封装程度正常
正常
低层
正常
支撑格局
完善
仅xlsx
仅xlsx
完善
开源协议
Apache-2.0
LGPLMIT
不开源
收费类型
免费
免费
免费
收费

评测阐明

版别与数据

一切代码的版别号根据上文中说到的最新安稳版别:


最新安稳版别号
NPOI
2.4.1
EPPlus
4.5.3.2
OpenXML2.9.1
Aspose.Cells
19.8.0

数据悉数根据我上篇文章运用的6万条/10列的数据,一共数据量19,166 KB。一切数据能够从这儿下载:https://github.com/sdcb/blog-data/tree/master/2019/20190821-generate-lorem-data

环境

项目
CPUE3-1230 v3 @ 3.30GHz
内存
24GB DDR3-1600 MHz (8GBx3)
操作系统Windows 10 1903 64
电源选项已设置为“高功用”
软件
LINQPad 6.0.18
运转时环境
.NET Core 3.0-preview8-28405-07

留意,LINQPad设置了optimize+,代码都是优化后履行的;代码都指定了Util.NewProcess = true;,保证每次运转都会在新进程中运转,不会相互影响。

我的功用测验函数介绍

IEnumerable Measure(Action action, int times = 5){ return Enumerable.Range(1, times).Select(i => { var sw = Stopwatch.StartNew;
long memory1 = GC.GetTotalMemory(true); long allocate1 = GC.GetTotalAllocatedByte平凡之路-.NET导出Excel的四种办法及评测s(true); { action; } long allocate2 = GC.GetTotalAllocatedBytes(true); long memory2 = GC.GetTotalMemory(true);
sw.Stop; return new { 次数 = i, 分配内存 = (allocate2 - allocate1).ToString("N0"), 内存进步 = (memory2 - memory1).ToString("N0"), 耗时 = sw.ElapsedMilliseconds, }; });}

除了时刻,内存占用实践也是十分十分重要、但简略被人疏忽的功用指标。咱们都认为“内存不值钱”,但——

  • 一旦拜访量大,内存就会瞬间上涨,导致频频GC,导致功用下降;

  • 内存高也会导致服务器分页,这时功用就会急剧下降;

  • 吞吐量下降会导致行列排满,此刻服务器就会报503等过错,客户就发现服务器“宕机了”。

(提示:除非你的客户真的乐意多花钱再晋级一下服务器,不然不要提“内存不值钱”。)

在我的功用测验函数中,运用了如下两个函数来测验内存占用:

  • GC.GetTotalAllocatedBytes(true) 获取分配内存大小

  • GC.GetTotalMemory(true) 获取占用内存大小

占用内存或许会比分配内存小,由于存在废物收回(GC),但GC会影响功用。

经过调用Measure函数,能够测得传入的action的耗时和内存占用。默许会调用5次,能够从5次测验成果中取出能反映功用的值。

测验基准

string Export(List data, string path){ PropertyInfo props = typeof(User).GetProperties; string noCache = ; for (var i = 0; i < props.Length; ++i) { noCache = props[i].Name; } for (var i = 0; i < data.Count; ++i) { for (var j = 0; j < props.Length; ++j) { noCache = props[j].GetValue(data[i]).ToString; } } return noCache;}

留意:

  • 我有意运用了反射,这契合咱们导出Excel代码简略、易学、好用、好扩展的乐意;

  • 我有意运用了泛型T,而不是实践类型,这也让这些代码简略扩展;

  • 里边的noCache用来躲避编译器优化删去代码的行为

测验成果:

次数
分配内存
内存进步
耗时
1
9,863,5208,712
156
29,852,5920
138
39,852,5920147
4
9,873,0969,240
136
59,853,936776
133

可见,根据反射操作6万/10列数据,每次需求分配约9MB内存,但这些内存都会被快速GC,终究内存进步较少。这些运用反射的代码运转耗时在130ms-150ms左右。

各个库的运用和功用体现

NPOI

void Export(List data, string path){ IWorkbook workbook = new XSSFWorkbook; ISheet sheet = workbook.CreateSheet("Sheet1");
var headRow = sheet.CreateRow(0); PropertyInfo props = typeof(User).GetProperties; for (var i = 0; i < props.Length; ++i) { headRow.CreateCell(i).SetCellValue(props[i].Name); } for (var i = 0; i < data.Count; ++i) { var row = sheet.CreateRow(i + 1); for (var j = 0; j < props.Length; ++j) { row.CreateCell(j).SetCellValue(props[j].GetValue(data[i]).ToString); } }
using var file = File.Create(path); workbook.Write(file);}

留意:

里边用到了XSSFWorkBook,其间XSSF这个前缀是从Java的POI库传过来的,全称是XML SpreadSheet Format。

这种前缀在NPOI包中很常见。

XSSFWorkbook供给了bool Dispose办法,但它未完成(因而千万别调用它):

功用测验成果:

次数
分配内存
内存进步
耗时
1
1,598,586,416537,0486590
21,589,239,7287,712
10155
31,589,232,056-5,36810309
4
1,589,237,0647,144
10355
51,589,245,0009,56010594

分配内存安稳在1.48GB的姿态,初次内存会进步524KB左右,后边趋于安稳。初次耗时6秒多,后边安稳在10秒多。

EPPlus

void Export(List data, string path){ using var stream = File.Create(path); using var excel = new ExcelPackage(stream); ExcelWorksheet sheet = excel.Workbook.Worksheets.Add("Sheet1"); PropertyInfo props = typeof(User).GetProperties; for (var i = 0; i < props.Length; ++i) { sheet.Cells[1, i + 1].Value = props[i].Name; } for (var i = 0; i < data.Count; ++i) { for (var j = 0; j < props.Length; ++j) { sheet.Cells[i + 2, j + 1].Value = props[j].GetValue(data[i]); } } excel.Save;}

留意,不同于NPOI/Aspose.Cells,EPPlus的下标是根据1的(而不是0)。

次数
分配内存
内存进步
耗时
1
534,970,328156,0483248
2533,610,23214,8962807
3533,595,9367,6平凡之路-.NET导出Excel的四种办法及评测482853
4
533,590,7764,408
2742
5533,598,44011,2802759

分配内存约508MB,耗时初次稍长,约3.2秒,后边安稳在2.7-2.8秒。

OpenXML

void Export(List data, string path){ using SpreadsheetDocument excel = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook);
WorkbookPart workbookPart = excel.AddWorkbookPart; workbookPart.Workbook = new Workbook;
WorksheetPart worksheetPart = workbookPart.AddNewPart; worksheetPart.Worksheet = new Worksheet(new SheetData);
Sheets sheets = excel.WorkbookPart.Workbook.AppendChild(new Sheets); Sheet sheet = new Sheet { Id = excel.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "Sheet1" }; sheets.Append(sheet); SheetData sheetData = worksheetPart.Worksheet.GetFirstChild;
PropertyInfo props = typeof(User).GetProperties; { // header var row = new Row { RowIndex = 1 }; sheetData.Append(row); row.Append(props.Select((prop, i) => new Cell { CellReference = ('A' + i - 1) + row.RowIndex.Value.ToString, CellValue = new CellValue(props[i].Name), DataType = new EnumValue(CellValues.String), })); } sheetData.Append(data.Select((item, i) => { var row = new Row { RowIndex = (uint)(i + 2) }; row.Append(props.Select((prop, j) => new Cell { CellReference = ('A' + j - 1) + row.RowIndex.Value.ToString, CellValue = new CellValue(props[j].GetValue(data[i]).ToString), DataType = new EnumValue(CellValues.String), })); return row; })); excel.Save;}

留意,由于`OpenXML`比较偏低层,东西比较杂乱,所以咱们慢慢说:

  • 关于一些方针,它需求创立相应的Part,如WorksheetPart;

  • Excel能够运用SharedStringTable来同享变量值,合适相同字符串十分多的场景。

    但此示例同享变量值收益很低,但会极大地添加代码杂乱性(普通用户或许很难写出),因而本示例未运用SharedStringTable;

  • 它根据单元格方位标识,如B3(第三行第二列),因而索引方法比EPPlus/NPOI都要杂乱;

  • 代码示例中运平凡之路-.NET导出Excel的四种办法及评测用'A' + i - 1来核算方位标识,因而这个示例不能用于超越26列(字母数)的数据;

  • 代码运用LINQ(而不是循环)来枚举一切行/列,能够让代码在现已十分杂乱的情况下,更简练一点;

    经测验,将LINQ改成for循环对功用成果改变影响极端细小。

测验成果如下:

次数
分配内存
内存进步
耗时
1
556,937,896145,8324009
2555,981,2163123783
3555,985,9362,7603884
4
555,984,3841,8723869
5555,989,1203,8803704

内存占用约530MB左右,榜初次比后边多1MB的姿态,耗时3.7-4.0秒之间。

Aspose.Cells

void Export(List data, string path){ using var excel = new Workbook; Worksheet sheet = excel.Worksheets["Sheet1"]; PropertyInfo props = typeof(User).GetProperties; for (var i = 0; i < props.Length; ++i) { sheet.Cells[0, i].Value = props[i].Name; } for (var i = 0; i < data.Count; ++i) { for (var j = 0; j < props.Length; ++j) { sheet.Cells[i + 1, j].Value = props[j].GetValue(data[i]); } } excel.Save(path);}

留意,Aspose.Cells像Excel软件相同,供给了Sheet1/Sheet2/Sheet3三个默许的作业表,因而取这三个作业表时,不要创立,而是取出来。

功用测验成果如下:

次数
分配内存
内存进步
耗时
1
404,004,9443,619,5203316
2357,931,6486,0482078
3357,934,7447,2162007
4
357,933,3766,2802017
5357,933,3606,4242007

Aspose.Cells初次占用内存385MB,用于3.3秒,后边每次下降为内存341MB,用时2.0秒。

总结

四种导出Excel库的横向评测数据如下,数据取5次数值的内存耗费中位数

,百分比以EPPlus的测验数据为100%基准:

次数
分配内存
内存占比
耗时
耗时占比
基准(仅反射)
9,853,9361.85%1334.82%
NPOI1,589,237,064297.83%10355375.32%
EPPlus533,598,440100%2759100%
OpenXML
555,985,936104.19%3884140.78%
Aspose357,933,36067%200772.74%

能够得出以下定论:

  1. Demo根据反射,但反射总损耗的功用不高,内存、耗时均不超越5%;

  2. NPOI的功用体现是一切项目中最差的,每次需求分配1.5GB的内存和超越10秒的耗时;

  3. EPPlus体现不错,内存和耗时在开源组中体现最佳;

  4. 收费的Aspose.Cells体现最佳,内存占用最低,用时也最短;

  5. 较为底层的OpenXML体现十分一般,比EPPlus要差,更不能与收费的Aspose混为一谈;

我的感触

在真的乐意测验一下之前,人们很简略信任自己的直觉。底层库,一般能带来更大的可扩展性,能做出上层库很难做的事来。底层库有时功用会更快,就像更底层的C/C++比上层的JavaScript更快相同。但工作也不都如此,如

  • 更高层的React.js能在功用大将较底层的DOM操作比下去

  • 数据库根据调集的操作也比根据游标的操作要快得多

在导出Excel这个比如中,我了解到Excel的xlsx格局是十分杂乱的、多个xml的调集。假如根据xml做笼统——也是很正常的做法,拼出6万/10列的数据,需求至少60万个xml标签做拼接,很明显这需求分配/糟蹋很多内存,因而功用上不来。

我根据以下几点无职责猜想:Aspose内部或许没xml做笼统,而是纯数据做笼统(就像React.js那样),然后再一致写入到Excel文件。因而功用能够到达其它库达不到的方针:

  1. Aspose.Cells对x早上好英文ml等完成相关技能只字未提(或许由于要支撑多种文件格局);

  2. Aspose.Cells是先在内存中创立,再写入文件/流(NPOI也是);

  3. Aspose.Cells创立Excel时要求客户直接运用Workbook类(NPOI也是);

  4. Aspose.Cells彻底躲藏了Excel的方位(如B3)信息,下标从0开端(NPOI也是)

比较这几点,NPOI也与Aspose.Cells有几分类似,但导出不到6MB的`Excel`它内存分配竟然高达1.5GB,是后者的444%!究竟迭代更新了这么多年了,代码质量我信任应该没问题。因而我再次无职责估测:这或许由于它是从Java那儿移植过来的。

我的挑选/引荐

在我做这个功用评测前,我一向运用的是EPPlus,由于我不喜欢NPOI有第三方依靠,也不喜欢NPOI那些“XSSF”之类的前缀命名,也明显不会去费心思写那么多费力不讨好的OpenXML代码。

更别提这次评测发现EPPlus的功用的确不错,仅有的缺陷便是它单元格下标从1开端的规划。即便如此,我仍是首选引荐EPPlus。

近期也常常运用Aspose.Cells这种商业库,它的功用强大平凡之路-.NET导出Excel的四种办法及评测,API明晰好用,这个评测也证明它的功用杰出。除了昂扬(https://purchase.aspose.com/pricing/cells/net)的价格,没其他缺陷了。乃有钱客户/老板的不贰之选!

出处:本文转载于微信大众号【DotNet骚操作】,作者【周杰DotNet 】

出处:微信大众号【DotNet骚操作】

微信不能留言,请点击原文链接去博客园留言。

原文链接:https://www.cnblogs.com/sdflysha/p/20190824-dotnet-excel-compare.html

二维码