之前很多项目所涉及Excel操作,都是基于读OLEDB写ExcelHelper。而我所采用的2种方式带来的很多问题,比如:

  1. 性能不佳、大数据量很容易Timeout。
  2. OLEDB你得知道工作溥名称、服务器必须安装Office组件。
  3. ExcelHelper虽极为简单操作,但总是在数据量小时罢工,生成的xls文件无法打开;且读也是基于OLEDB操作。

由于以上种种原因,而后我开始慢慢寻找新的解决方案,且必须满足以下我经常遇到的问题:

  1. 基本创建、导入、导出,支持xls和xlsx。
  2. 必须开源。
  3. 尽可能不需要Office组件。
  4. 支持较为完整的操作,比如:单元格格式、图片、图表、加密、数据有效性。
  5. 最好有Linq支持。

基于以上三点,我在新的项目尝试新选择,从NPOI和EPPLUS中选择。

NPOI

作者应该是天朝人,且是基于POI Java版本在.NET的实现,从NPOI 2版本开始支持OOXML格式,这也就是说将无条件对Excel、Word、PPT等文件格式的支持,而目前的版本分别支持Excel和WORD操作。

两个重要链接:源代码作者官网BLOG(中文 )

NPOI从官网上看只能对单元格式、数据格式、公式进行操作,这已然足以解决大部分需求。

由于作者BLOG已经提供大量的中文示例,不再进行赘述。

EPPLUS

相比较于NPOI她是对Excel比较专一的操作,且支持我上面提供所有功能。理所当然EPPLUS也是基于OOXML格式,但目前只对Excel支持,相对于专一。

支持非常丰富,从创建、导入、导出、单元格式格式、公式、图表、批注、加密、VBA等等,堪称对Excel完美支持。特别是支持Linq语法。

NPOI和EPPLUS的输出大量数据时比较

PS:nopi在NuGet还是1.x的版本。

以下测试条件:列数、单元格内容完全等同的情况下:

行数 NPOI时间 EPPLUS时间 NPOI文件大小 EPPLUS文件大小
10 252ms 28ms 8.9 KB 3.8 KB
1000 311ms 114ms 64.9 KB 26.4 KB
10000 893ms 508ms 512 KB 225 KB

Excel2007以上的版本可以超过65535条纪录。

以下是代码片断:

NPOI Code:

log.Info("begin npoi");
MemoryStream ms = new MemoryStream();
IWorkbook workbook = new XSSFWorkbook();
ISheet sheet = workbook.CreateSheet("Sheet1");
for (int i = 0; i < RowCount; i++)
{
    // 第一个Row要用Create的
    sheet.CreateRow(i).CreateCell(0).SetCellValue("This is a Sample");
    // 第二个Row之后可直接使用Get
    sheet.GetRow(i).CreateCell(1).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(2).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(3).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(4).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(5).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(6).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(7).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(8).SetCellValue("This is a Sample");
    sheet.GetRow(i).CreateCell(9).SetCellValue("This is a Sample");
}
workbook.Write(ms);
log.Info("end npoi");
// download 
Response.ContentType = "application/download";
Response.AddHeader("Content-Disposition", "attachment; filename=file_npoi.xlsx");
Response.Clear();
Response.BinaryWrite(ms.GetBuffer());
ms.Close();
ms.Dispose();

EPPLUS Code:

log.Info("begin epplus");
using (ExcelPackage pck = new ExcelPackage())
{
    var ws = pck.Workbook.Worksheets.Add("Sheet1");
    for (int i = 1; i <= RowCount; i++)
    {
        ws.Cells[i, 1].Value = "This is a Sample";
        ws.Cells[i, 2].Value = "This is a Sample";
        ws.Cells[i, 3].Value = "This is a Sample";
        ws.Cells[i, 4].Value = "This is a Sample";
        ws.Cells[i, 5].Value = "This is a Sample";
        ws.Cells[i, 6].Value = "This is a Sample";
        ws.Cells[i, 7].Value = "This is a Sample";
        ws.Cells[i, 8].Value = "This is a Sample";
        ws.Cells[i, 9].Value = "This is a Sample";
        ws.Cells[i, 10].Value = "This is a Sample";
    }
    log.Info("end epplus");
    Response.ContentType = "application/download";
    Response.AddHeader("Content-Disposition", "attachment; filename=file_epplus.xlsx");
    Response.Clear();
    Response.BinaryWrite(pck.GetAsByteArray());
}

速度和文件大小上NPOI几乎看不到EPPLUS车尾灯。

有个很奇怪的现象,2个组件都是采用OOXML,为何文件大小始终差一倍如此之多呢?

好吧,暂且先到这,目前新项目中采用EPPLUS,其实从可用性上NPOI更好选择,基本上可以满足绝大部分需求且还支持Word。