|
| 1 | +## MiniExcel |
| 2 | +[](https://www.nuget.org/packages/MiniExcel) |
| 3 | +[](https://www.nuget.org/packages/MiniExcel) |
| 4 | +[](https://github.com/mini-software/MiniExcel) |
| 5 | +[](https://gitee.com/dotnetchina/MiniExcel) |
| 6 | +[](https://www.nuget.org/packages/MiniExcel) |
| 7 | +[](https://deepwiki.com/mini-software/MiniExcel) |
1 | 8 |
|
2 | 9 | This project is part of the [.NET Foundation](https://dotnetfoundation.org/projects/project-detail/miniexcel) and operates under their code of conduct. |
3 | 10 |
|
4 | 11 | --- |
5 | 12 |
|
6 | | -### Introduction |
| 13 | +MiniExcel is a simple and efficient Excel processing tool for .NET, specifically designed to minimize memory usage. |
7 | 14 |
|
8 | | -MiniExcel is simple and efficient to avoid OOM's .NET processing Excel tool. |
| 15 | +At present, most popular frameworks need to load all the data from an Excel document into memory to facilitate operations, but this may cause memory consumption problems. MiniExcel's approach is different: the data is processed row by row in a streaming manner, reducing the original consumption from potentially hundreds of megabytes to just a few megabytes, effectively preventing out-of-memory(OOM) issues. |
9 | 16 |
|
10 | | -At present, most popular frameworks need to load all the data into the memory to facilitate operation, but it will cause memory consumption problems. MiniExcel tries to use algorithm from a stream to reduce the original 1000 MB occupation to a few MB to avoid OOM(out of memory). |
| 17 | +```mermaid |
| 18 | +flowchart LR |
| 19 | + A1(["Excel analysis<br>process"]) --> A2{{"Unzipping<br>XLSX file"}} --> A3{{"Parsing<br>OpenXML"}} --> A4{{"Model<br>conversion"}} --> A5(["Output"]) |
11 | 20 |
|
12 | | - |
| 21 | + B1(["Other Excel<br>Frameworks"]) --> B2{{"Memory"}} --> B3{{"Memory"}} --> B4{{"Workbooks &<br>Worksheets"}} --> B5(["All rows at<br>the same time"]) |
13 | 22 |
|
| 23 | + C1(["MiniExcel"]) --> C2{{"Stream"}} --> C3{{"Stream"}} --> C4{{"POCO or dynamic"}} --> C5(["Deferred execution<br>row by row"]) |
14 | 24 |
|
15 | | -### Features |
| 25 | + classDef analysis fill:#D0E8FF,stroke:#1E88E5,color:#0D47A1,font-weight:bold; |
| 26 | + classDef others fill:#FCE4EC,stroke:#EC407A,color:#880E4F,font-weight:bold; |
| 27 | + classDef miniexcel fill:#E8F5E9,stroke:#388E3C,color:#1B5E20,font-weight:bold; |
16 | 28 |
|
17 | | -- Low memory consumption, avoid OOM (out of memory) and full GC |
18 | | -- Supports real time operation of each row of data |
19 | | -- Supports LINQ deferred execution, it can do low-consumption, fast paging and other complex queries |
20 | | -- Lightweight, without Microsoft Office installed, no COM+, DLL size is less than 400KB |
21 | | -- Easy API style to read/write/fill excel |
| 29 | + class A1,A2,A3,A4,A5 analysis; |
| 30 | + class B1,B2,B3,B4,B5 others; |
| 31 | + class C1,C2,C3,C4,C5 miniexcel; |
| 32 | +``` |
22 | 33 |
|
23 | | -### Get Started |
| 34 | +### Features |
24 | 35 |
|
25 | | -- [Import/Query Excel](#getstart1) |
| 36 | +- Minimizes memory consumption, preventing out-of-memory (OOM) errors and avoiding full garbage collections |
| 37 | +- Enables real-time, row-level data operations for better performance on large datasets |
| 38 | +- Supports LINQ with deferred execution, allowing for fast, memory-efficient paging and complex queries |
| 39 | +- Lightweight, without the need for Microsoft Office or COM+ components, and a size under 800KB |
| 40 | +- Simple and intuitive API to import, export, and template Excel worksheets |
26 | 41 |
|
27 | | -- [Export/Create Excel](#getstart2) |
| 42 | +### Quickstart |
28 | 43 |
|
29 | | -- [Excel Template](#getstart3) |
| 44 | +#### Importing |
30 | 45 |
|
31 | | -- [Excel Column Name/Index/Ignore Attribute](#getstart4) |
| 46 | +You can query worksheets and map the data either to strongly typed classes or dynamic objects: |
32 | 47 |
|
33 | | -- [Examples](#getstart5) |
| 48 | +```csharp |
| 49 | +public class UserAccount |
| 50 | +{ |
| 51 | + public Guid ID { get; set; } |
| 52 | + public string Name { get; set; } |
| 53 | + public DateTime DateOfBirth { get; set; } |
| 54 | + public int Age { get; set; } |
| 55 | + public bool Vip { get; set; } |
| 56 | + public decimal Points { get; set; } |
| 57 | +} |
34 | 58 |
|
| 59 | +var userRows = MiniExcel.Query<UserAccount>(path); |
35 | 60 |
|
| 61 | +// or simply |
36 | 62 |
|
37 | | -### Installation |
| 63 | +var rows = MiniExcel.Query(path); |
| 64 | +``` |
38 | 65 |
|
39 | | -You can install the package [from NuGet](https://www.nuget.org/packages/MiniExcel) |
| 66 | +#### Exporting |
40 | 67 |
|
41 | | -### Release Notes |
| 68 | +There are multiple ways to export data to an Excel document: |
42 | 69 |
|
43 | | -Please Check [Release Notes](docs) |
| 70 | +```csharp |
| 71 | +// From strongly typed objects |
44 | 72 |
|
45 | | -### TODO |
| 73 | +var values = new[] |
| 74 | +{ |
| 75 | + new { Name = "MiniExcel", Value = 1 }, |
| 76 | + new { Name = "Github", Value = 2 } |
| 77 | +}; |
| 78 | +MiniExcel.SaveAs(yourPath, values); |
46 | 79 |
|
47 | | -Please Check [TODO](https://github.com/mini-software/MiniExcel/projects/1?fullscreen=true) |
48 | 80 |
|
49 | | -### Performance |
| 81 | +// From anonymous objects |
50 | 82 |
|
51 | | -The code for the benchmarks can be found in [MiniExcel.Benchmarks](https://github.com/mini-software/MiniExcel/tree/master/benchmarks/MiniExcel.Benchmarks). |
52 | | -To run all the benchmarks use: |
| 83 | +public class TestType |
| 84 | +{ |
| 85 | + public string Name { get; set; } |
| 86 | + public int Value { get; set; } |
| 87 | +} |
53 | 88 |
|
54 | | -```bash |
55 | | -dotnet run -project .\benchmarks\MiniExcel.Benchmarks -c Release -f net9.0 -filter * --join |
56 | | -``` |
| 89 | +TestType[] values = |
| 90 | +[ |
| 91 | + new TestType { Name = "MiniExcel", Value = 1 }, |
| 92 | + new TestType { Name = "Github", Value = 2 } |
| 93 | +]; |
| 94 | +MiniExcel.SaveAs(yourPath, values); |
57 | 95 |
|
58 | | -Hardware and settings used are the following: |
59 | | -``` |
60 | | -BenchmarkDotNet v0.15.0, Linux Ubuntu 24.04.2 LTS (Noble Numbat) |
61 | | -AMD EPYC 7763, 1 CPU, 4 logical and 2 physical cores |
62 | | -.NET SDK 9.0.300 |
63 | | - [Host] : .NET 9.0.5 (9.0.525.21509), X64 RyuJIT AVX2 |
64 | | - ShortRun : .NET 9.0.5 (9.0.525.21509), X64 RyuJIT AVX2 |
65 | | -``` |
66 | 96 |
|
67 | | -#### Import/Query Excel |
| 97 | +//From a IEnumerable<IDictionary<string, object>> |
68 | 98 |
|
69 | | -The file used to test performance is [**Test1,000,000x10.xlsx**](https://github.com/mini-software/MiniExcel/tree/master/benchmarks/MiniExcel.Benchmarks/Test1%2C000%2C000x10.xlsx), a 32MB document containing 1,000,000 rows * 10 columns whose cells are filled with the string "HelloWorld". |
| 99 | +List<Dictionary<string, object>>() dicts = |
| 100 | +[ |
| 101 | + new Dictionary<string, object> { { "Name", "MiniExcel" }, { "Value", 1 } }, |
| 102 | + new Dictionary<string, object> { { "Name", "Github" }, { "Value", 2 } } |
| 103 | +]; |
| 104 | +MiniExcel.SaveAs(yourPath, dicts); |
70 | 105 |
|
71 | | -| Method | Mean | StdDev | Error | Gen0 | Gen1 | Gen2 | Allocated | |
72 | | -|--------------------------------------|-----------------:|---------------:|-----------------:|------------:|------------:|----------:|--------------:| |
73 | | -| 'MiniExcel QueryFirst' | 63.70 μs | 0.337 μs | 6.144 μs | 2.9297 | 2.7669 | - | 49.67 KB | |
74 | | -| 'ExcelDataReader QueryFirst' | 5,010,679.51 μs | 53,245.186 μs | 971,390.400 μs | 105000.0000 | 333.3333 | - | 1717272.56 KB | |
75 | | -| 'MiniExcel Query' | 9,172,286.91 μs | 12,805.326 μs | 233,616.824 μs | 448500.0000 | 4666.6667 | - | 7327883.36 KB | |
76 | | -| 'ExcelDataReader Query' | 10,609,617.09 μs | 29,055.953 μs | 530,088.745 μs | 275666.6667 | 68666.6667 | - | 4504691.87 KB | |
77 | | -| 'Epplus QueryFirst' | 13,770,656.24 μs | 45,909.809 μs | 837,565.827 μs | 174333.3333 | 88833.3333 | 4333.3333 | 3700587.76 KB | |
78 | | -| 'Epplus Query' | 19,257,306.83 μs | 63,117.956 μs | 1,151,506.486 μs | 452333.3333 | 90500.0000 | 5333.3333 | 8223933.16 KB | |
79 | | -| 'ClosedXml Query' | 31,070,263.83 μs | 342,973.671 μs | 6,257,116.502 μs | 401666.6667 | 104166.6667 | 3333.3333 | 6822559.68 KB | |
80 | | -| 'ClosedXml QueryFirst' | 31,141,877.48 μs | 21,006.538 μs | 383,237.459 μs | 402166.6667 | 104833.3333 | 3833.3333 | 6738357.8 KB | |
81 | | -| 'OpenXmlSDK QueryFirst' | 31,750,686.63 μs | 263,328.569 μs | 4,804,093.357 μs | 374666.6667 | 374500.0000 | 3166.6667 | 6069266.96 KB | |
82 | | -| 'OpenXmlSDK Query' | 32,919,119.46 μs | 411,395.682 μs | 7,505,388.691 μs | 374666.6667 | 374500.0000 | 3166.6667 | 6078467.83 KB | |
83 | 106 |
|
| 107 | +// Directly from a IDataReader |
84 | 108 |
|
85 | | -#### Export/Create Excel |
| 109 | +using var connection = yourConnectionProvider.GetConnection(); |
| 110 | +connection.Open(); |
86 | 111 |
|
87 | | -Logic: create a total of 10,000,000 "HelloWorld" cells Excel document |
| 112 | +using var cmd = connection.CreateCommand(); |
| 113 | +cmd.CommandText = """ |
| 114 | + SELECT 'MiniExcel' AS "Name", 1 AS "Value" |
| 115 | + UNION ALL |
| 116 | + SELECT 'Github', 2 |
| 117 | + """; |
88 | 118 |
|
89 | | -| Method | Mean | StdDev | Error | Gen0 | Gen1 | Gen2 | Allocated | |
90 | | -|----------------------------------------------|---------:|---------:|---------:|------------:|------------:|----------:|----------:| |
91 | | -| 'MiniExcel Create Xlsx' | 4.427 s | 0.0056 s | 0.1023 s | 251666.6667 | 1833.3333 | 1666.6667 | 3.92 GB | |
92 | | -| 'OpenXmlSdk Create Xlsx by DOM mode' | 22.729 s | 0.1226 s | 2.2374 s | 307000.0000 | 306833.3333 | 3833.3333 | 6.22 GB | |
93 | | -| 'ClosedXml Create Xlsx' | 22.851 s | 0.0190 s | 0.3473 s | 195500.0000 | 54500.0000 | 4166.6667 | 4.48 GB | |
94 | | -| 'Epplus Create Xlsx' | 23.027 s | 0.0088 s | 0.1596 s | 89000.0000 | 17500.0000 | 6000.0000 | 2.51 GB | |
| 119 | +using var reader = cmd.ExecuteReader(); |
| 120 | +MiniExcel.SaveAs(yourPath, reader); |
95 | 121 |
|
96 | | -Warning: these results may be outdated. You can find the benchmarks for the latest release [here](https://github.com/mini-software/MiniExcel/tree/master/benchmarks/results). |
97 | 122 |
|
| 123 | +// From a DataTable |
98 | 124 |
|
99 | | -### Documents |
| 125 | +var table = new DataTable(); |
| 126 | +table.Columns.Add("Name", typeof(string)); |
| 127 | +table.Columns.Add("Value", typeof(int)); |
| 128 | +table.Rows.Add("MiniExcel", 1); |
| 129 | +table.Rows.Add("Github", 2); |
100 | 130 |
|
101 | | -https://github.com/mini-software/MiniExcel |
| 131 | +MiniExcel.SaveAs(path, table); |
| 132 | +``` |
0 commit comments