(一) MiniExcel 读/导入 Excel

1.了解MiniExcel

MiniExcel是简单、高效避免OOM的NET处理Excel查、写、填充数据工具。目前主流框架大多需要将数据全载入到内存方便操作,但这会导致内存消耗问题,MiniExcel 尝试以 Stream 角度写底层算法逻辑,能让原本1000多MB占用降低到几MB,避免内存不够情况。
image

image

特点

  • 低内存耗用,避免OOM、频繁 Full GC 情况
  • 支持即时操作每行数据
  • 兼具搭配 LINQ 延迟查询特性,能办到低消耗、快速分页等复杂查询
  • 轻量,不需要安装 Microsoft Office、COM+,DLL小于150KB
  • 简便操作的 API 风格

2.常见插件对比

miniexcel

不需要在内存导入数据,耗时在5s左右,内存30MB左右
image
支持csv导出
image
多表导出
image
存图片
image

closedxml

本质上是把数据都放到内存然后进行处理,内存占用极高,在7GB以上,时间也耗时特别久在50秒以上
image

导入、查询 Excel 比较

逻辑 : 以 Test1,000,000x10.xlsx 做基准与主流框架做性能测试,总共 1,000,000 行 * 10 列笔 "HelloWorld",文件大小 23 MB
image

导出、创建 Excel 比较

image

3. 读/导入 Excel

1. Query 查询 Excel 返回强型别 IEnumerable 数据

泛型

public class UserAccount
{
    public Guid ID { get; set; }
    public string Name { get; set; }
    public DateTime BoD { get; set; }
    public int Age { get; set; }
    public bool VIP { get; set; }
    public decimal Points { get; set; }
}

var rows = MiniExcel.Query<UserAccount>(path);

// or

using (var stream = File.OpenRead(path))
    var rows = stream.Query<UserAccount>();

2.Query 查询 Excel 返回Dynamic IEnumerable 数据

ToList()


var rows = MiniExcel.Query(path).ToList();

// or 
using (var stream = File.OpenRead(path))
{
    var rows = stream.Query().ToList();
                
    Assert.Equal("MiniExcel", rows[0].A);
    Assert.Equal(1, rows[0].B);
    Assert.Equal("Github", rows[1].A);
    Assert.Equal(2, rows[1].B);
}

3.查询数据以第一行数据当Key

useHeaderRow:true


var rows = MiniExcel.Query(useHeaderRow:true).ToList();

// or

using (var stream = File.OpenRead(path))
{
    var rows = stream.Query(useHeaderRow:true).ToList();

    Assert.Equal("MiniExcel", rows[0].Column1);
    Assert.Equal(1, rows[0].Column2);
    Assert.Equal("Github", rows[1].Column1);
    Assert.Equal(2, rows[1].Column2);
}

4. Query 查询支援延迟加载(Deferred Execution),能配合LINQ First/Take/Skip办到低消耗、高效率复杂查询

var row = MiniExcel.Query(path).First();
Assert.Equal("HelloWorld", row.A);

// or

using (var stream = File.OpenRead(path))
{
    var row = stream.Query().First();
    Assert.Equal("HelloWorld", row.A);
}

image

5.查询指定 Sheet 名称

MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");

6.查询所有 Sheet 名称跟数据

var sheetNames = MiniExcel.GetSheetNames(path);
foreach (var sheetName in sheetNames)
{
    var rows = MiniExcel.Query(path, sheetName: sheetName);
}

7.查询所有栏(列)

var columns = MiniExcel.GetColumns(path); // e.g result : ["A","B"...]

or

var columns = MiniExcel.GetColumns(path, useHeaderRow: true); 
// e.g result : ["excel表实际的列名称","excel表实际的列名称"...]

var cnt = columns.Count;  // get column count

8.Dynamic Query 转成 IDictionary<string,object> 数据

foreach(IDictionary<string,object> row in MiniExcel.Query(path))
{
    //..
}

// or 
var rows = MiniExcel.Query(path).Cast<IDictionary<string,object>>(); 

9.Query 读 Excel 返回 DataTable

提醒 : 不建议使用,因为DataTable会将数据全载入内存,失去MiniExcel低内存消耗功能。
var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);
image

10.指定单元格开始读取数据

MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
image

11.合并的单元格填充

注意 : 效率相对于没有使用合并填充来说差
底层原因 : OpenXml 标准将 mergeCells 放在文件最下方,导致需要遍历两次 sheetxml

	var config = new OpenXmlConfiguration()
	{
		FillMergedCells = true
	};
	var rows = MiniExcel.Query(path, configuration: config);

image

支持不固定长宽多行列填充
image

12.读取大文件硬盘缓存 (Disk-Base Cache - SharedString)

概念 : MiniExcel 当判断文件 SharedString 大小超过 5MB,预设会使用本地缓存,如 10x100000.xlsx(一百万笔数据),读取不开启本地缓存需要最高内存使用约195MB,开启后降为65MB。但要特别注意,此优化是以时间换取内存减少,所以读取效率会变慢,此例子读取时间从 7.4 秒提高到 27.2 秒,假如不需要能用以下代码关闭硬盘缓存

var config = new OpenXmlConfiguration { EnableSharedStringCache = false };
MiniExcel.Query(path,configuration: config)

也能使用 SharedStringCacheSize 调整 sharedString 文件大小超过指定大小才做硬盘缓存

var config = new OpenXmlConfiguration { SharedStringCacheSize=500*1024*1024 };
MiniExcel.Query(path, configuration: config);

image
image

posted @ 2024-04-09 14:56  huihui不会写代码  阅读(1864)  评论(0)    收藏  举报