Proyek ini merupakan bagian dari .NET Foundation dan beroperasi di bawah kode etik mereka.
English | 简体中文 | 繁體中文 | 日本語 | 한국어 | हिन्दी | ไทย | Français | Deutsch | Español | Italiano | Русский | Português | Nederlands | Polski | العربية | فارسی | Türkçe | Tiếng Việt | Bahasa Indonesia
Pengantar
MiniExcel adalah alat pemrosesan Excel yang sederhana dan efisien untuk .NET, khusus dirancang untuk meminimalkan penggunaan memori.
Saat ini, sebagian besar framework populer perlu memuat semua data dari dokumen Excel ke dalam memori untuk memudahkan operasi, namun hal ini dapat menyebabkan masalah konsumsi memori. Pendekatan MiniExcel berbeda: data diproses baris demi baris secara streaming, mengurangi konsumsi asli dari ratusan megabyte menjadi hanya beberapa megabyte, secara efektif mencegah masalah out-of-memory (OOM).
flowchart LR
A1(["Excel analysis
process"]) --> A2{{"Unzipping
XLSX file"}} --> A3{{"Parsing
OpenXML"}} --> A4{{"Model
conversion"}} --> A5(["Output"]) B1(["Other Excel
Frameworks"]) --> B2{{"Memory"}} --> B3{{"Memory"}} --> B4{{"Workbooks &
Worksheets"}} --> B5(["All rows at
the same time"])
C1(["MiniExcel"]) --> C2{{"Stream"}} --> C3{{"Stream"}} --> C4{{"POCO or dynamic"}} --> C5(["Deferred execution
row by row"])
classDef analysis fill:#D0E8FF,stroke:#1E88E5,color:#0D47A1,font-weight:bold;
classDef others fill:#FCE4EC,stroke:#EC407A,color:#880E4F,font-weight:bold;
classDef miniexcel fill:#E8F5E9,stroke:#388E3C,color:#1B5E20,font-weight:bold;
class A1,A2,A3,A4,A5 analysis;
class B1,B2,B3,B4,B5 others;
class C1,C2,C3,C4,C5 miniexcel;
Fitur
- Meminimalkan konsumsi memori, mencegah kesalahan out-of-memory (OOM) dan menghindari garbage collection penuh
- Mendukung operasi data tingkat baris secara real-time untuk kinerja yang lebih baik pada dataset besar
- Mendukung LINQ dengan eksekusi tertunda, memungkinkan pemrosesan paging yang cepat dan efisien memori serta kueri kompleks
- Ringan, tanpa perlu Microsoft Office atau komponen COM+, dan ukuran DLL kurang dari 500KB
- Gaya API yang sederhana dan intuitif untuk membaca/menulis/mengisi excel
Versi 2.0 pratinjau
Kami sedang mengembangkan versi MiniExcel masa depan, dengan API baru yang modular dan terfokus,
paket nuget terpisah untuk fungsionalitas Core dan Csv, dukungan penuh untuk kueri streaming asinkron melalui IAsyncEnumerable,
dan masih banyak lagi yang akan datang! Paket-paket ini akan tersedia dalam versi pra-rilis, jadi silakan cek dan berikan masukan kepada kami!
Jika Anda melakukannya, pastikan juga untuk melihat dokumentasi baru dan catatan upgrade.
Memulai
Instalasi
Anda dapat menginstal paket dari NuGet
Catatan Rilis
Silakan cek Catatan Rilis
TODO
Silakan Periksa TODO
Performa
Kode untuk benchmark dapat ditemukan di MiniExcel.Benchmarks.
File yang digunakan untuk menguji performa adalah Test1,000,000x10.xlsx, sebuah dokumen 32MB yang berisi 1.000.000 baris * 10 kolom yang setiap selnya diisi string "HelloWorld".
Untuk menjalankan semua benchmark gunakan:
dotnet run -project .\benchmarks\MiniExcel.Benchmarks -c Release -f net9.0 -filter * --join
Anda dapat menemukan hasil benchmark untuk rilis terbaru di sini.Query/Impor Excel
#### 1. Jalankan query dan petakan hasilnya ke IEnumerable yang bertipe kuat [[Coba di sini]](https://dotnetfiddle.net/w5WD1J)
Disarankan menggunakan Stream.Query karena efisiensinya yang lebih baik.
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(path);
// or
using (var stream = File.OpenRead(path))
var rows = stream.Query();

#### 2. Jalankan query dan petakan ke daftar objek dinamis tanpa menggunakan head [[Coba di sini]](https://dotnetfiddle.net/w5WD1J)
- kunci dinamis adalah
A.B.C.D..
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. Jalankan kueri dengan baris header pertama [[Coba di sini]](https://dotnetfiddle.net/w5WD1J)catatan : nama kolom yang sama menggunakan yang paling kanan
Input Excel :
| Kolom1 | Kolom2 | |-----------|---------| | MiniExcel | 1 | | Github | 2 |
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. Dukungan Query Ekstensi LINQ First/Take/Skip ...dan lain-lainQuery First
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);
}
Performa antara MiniExcel/ExcelDataReader/ClosedXML/EPPlus

#### 5. Query berdasarkan nama sheet
MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");
#### 6. Query semua nama sheet dan barisvar sheetNames = MiniExcel.GetSheetNames(path);
foreach (var sheetName in sheetNames)
{
var rows = MiniExcel.Query(path, sheetName: sheetName);
}
#### 7. Dapatkan Kolomvar columns = MiniExcel.GetColumns(path); // e.g result : ["A","B"...]var cnt = columns.Count; // get column count
#### 8. Dynamic Query mengubah baris menjadi IDictionaryforeach(IDictionary row in MiniExcel.Query(path))
{
//..
}// or
var rows = MiniExcel.Query(path).Cast>();
// or Query specified ranges (capitalized)
// A2 represents the second row of column A, C3 represents the third row of column C
// If you don't want to restrict rows, just don't include numbers
var rows = MiniExcel.QueryRange(path, startCell: "A2", endCell: "C3").Cast>();
#### 9. Query Excel mengembalikan DataTableTidak direkomendasikan, karena DataTable akan memuat semua data ke dalam memori dan kehilangan fitur konsumsi memori rendah dari MiniExcel.
``C#
var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);

#### 10. Tentukan sel untuk mulai membaca data
csharp MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
csharp var config = new OpenXmlConfiguration() { FillMergedCells = true }; var rows = MiniExcel.Query(path, configuration: config);tidak menggunakan pengisian gabungan#### 11. Isi Sel yang Digabungkan
Catatan: Efisiensinya lebih lambat dibandingkan
Alasan: Standar OpenXml menempatkan mergeCells di bagian bawah file, sehingga perlu melakukan foreach pada sheetxml dua kali

mendukung pengisian multi-baris dan kolom dengan panjang dan lebar variabel

#### 12. Membaca file besar menggunakan cache berbasis disk (Disk-Base Cache - SharedString)
Jika ukuran SharedStrings melebihi 5 MB, secara default MiniExcel akan menggunakan cache disk lokal, misal, 10x100000.xlsx(satu juta baris data), saat cache disk dinonaktifkan penggunaan memori maksimum adalah 195MB, namun jika cache disk diaktifkan hanya membutuhkan 65MB. Perlu dicatat, optimisasi ini membutuhkan biaya efisiensi, sehingga kasus ini akan meningkatkan waktu baca dari 7,4 detik menjadi 27,2 detik. Jika Anda tidak membutuhkannya, Anda dapat menonaktifkan cache disk dengan kode berikut:
csharp
var config = new OpenXmlConfiguration { EnableSharedStringCache = false };
MiniExcel.Query(path,configuration: config)
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);Anda dapat menggunakanSharedStringCacheSizeuntuk mengubah ukuran file sharedString melebihi ukuran yang ditentukan untuk caching disk


Membuat/Mengekspor Excel
- Harus merupakan tipe non-abstrak dengan konstruktor publik tanpa parameter.
- MiniExcel mendukung parameter IEnumerable Deferred Execution, Jika Anda ingin menggunakan memori seminimal mungkin, mohon jangan gunakan metode seperti ToList
misalnya : ToList atau tidak penggunaan memori

#### 1. Tipe anonim atau strongly type [[Coba di sini]](https://dotnetfiddle.net/w5WD1J)
csharp
var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx");
MiniExcel.SaveAs(path, new[] {
new { Column1 = "MiniExcel", Column2 = 1 },
new { Column1 = "Github", Column2 = 2}
});
csharp var values = new List#### 2.IEnumerable>
csharp MiniExcel.SaveAs(path, reader);Buat Hasil File :Direkomendasikan| Kolom1 | Kolom2 | |-----------|---------| | MiniExcel | 1 | | Github | 2 |
#### 3. IDataReader
, dapat menghindari memuat semua data ke dalam memori

DataReader mengekspor beberapa sheet (disarankan oleh Dapper ExecuteReader)
csharp
using (var cnn = Connection)
{
cnn.Open();
var sheets = new Dictionarycsharp var path = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.xlsx"); var table = new DataTable(); { table.Columns.Add("Column1", typeof(string)); table.Columns.Add("Column2", typeof(decimal)); table.Rows.Add("MiniExcel", 1); table.Rows.Add("Github", 2); }#### 4. DatatableTidak direkomendasikan, karena akan memuat semua data ke dalam memoriDataTable menggunakan Caption untuk nama kolom terlebih dahulu, lalu menggunakan nama kolom
MiniExcel.SaveAs(path, table);
csharp using (var connection = GetConnection(connectionString)) { var rows = connection.Query( new CommandDefinition( @"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2" , flags: CommandFlags.NoCache) ); // Note: QueryAsync will throw close connection exception MiniExcel.SaveAs(path, rows); }#### 5. Dapper QueryCommandDefinition + CommandFlags.NoCacheTerima kasih @shaofing #552 , silakan gunakan
Kode di bawah ini akan memuat semua data ke dalam memoricsharp
using (var connection = GetConnection(connectionString))
{
var rows = connection.Query(@"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2");
MiniExcel.SaveAs(path, rows);
}
#### 6. SaveAs ke MemoryStream [[Coba di sini]](https://dotnetfiddle.net/JOen0e)
csharp
using (var stream = new MemoryStream()) //support FileStream,MemoryStream ect.
{
stream.SaveAs(values);
}
misal: api untuk ekspor excelcsharp
public IActionResult DownloadExcel()
{
var values = new[] {
new { Column1 = "MiniExcel", Column2 = 1 },
new { Column1 = "Github", Column2 = 2}
};var memoryStream = new MemoryStream(); memoryStream.SaveAs(values); memoryStream.Seek(0, SeekOrigin.Begin); return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { FileDownloadName = "demo.xlsx" }; }
#### 7. Membuat Beberapa Lembar
csharp
// 1. Dictionary// 2. DataSet var sheets = new DataSet(); sheets.Add(UsersDataTable); sheets.Add(DepartmentDataTable); //.. MiniExcel.SaveAs(path, sheets);

#### 8. Opsi TableStyles
Gaya bawaan

Tanpa konfigurasi gaya
csharp
var config = new OpenXmlConfiguration()
{
TableStyles = TableStyles.None
};
MiniExcel.SaveAs(path, value,configuration:config);
csharp MiniExcel.SaveAs(path, value, configuration: new OpenXmlConfiguration() { AutoFilter = false });OpenXmlConfiguration.AutoFilter#### 9. AutoFilter
Sejak v0.19.0
dapat mengaktifkan/nonaktifkan AutoFilter, nilai default adalahtrue, dan cara mengatur AutoFilter:
#### 10. Membuat Gambar
csharp
var value = new[] {
new { Name="github",Image=File.ReadAllBytes(PathHelper.GetFile("images/github_logo.png"))},
new { Name="google",Image=File.ReadAllBytes(PathHelper.GetFile("images/google_logo.png"))},
new { Name="microsoft",Image=File.ReadAllBytes(PathHelper.GetFile("images/microsoft_logo.png"))},
new { Name="reddit",Image=File.ReadAllBytes(PathHelper.GetFile("images/reddit_logo.png"))},
new { Name="statck_overflow",Image=File.ReadAllBytes(PathHelper.GetFile("images/statck_overflow_logo.png"))},
};
MiniExcel.SaveAs(path, value);
csharp var mergedFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");byte[]#### 11. Ekspor File Byte Array
Sejak versi 1.22.0, ketika tipe nilai adalah
maka sistem akan menyimpan path file di sel secara default, dan saat impor sistem dapat mengonversinya menjadibyte[]. Dan jika Anda tidak ingin menggunakannya, Anda dapat mengaturOpenXmlConfiguration.EnableConvertByteArraykefalse, ini dapat meningkatkan efisiensi sistem.byte[]
Sejak versi 1.22.0, ketika tipe nilai adalah
maka sistem akan menyimpan path file di sel secara default, dan saat impor sistem dapat mengonversinya menjadibyte[]. Dan jika Anda tidak ingin menggunakannya, Anda dapat mengaturOpenXmlConfiguration.EnableConvertByteArraykefalse, ini dapat meningkatkan efisiensi sistem.xlsx
#### 12. Menggabungkan sel yang sama secara vertikal
Fungsi ini hanya didukung dalam format
dan menggabungkan sel secara vertikal di antara tag @merge dan @endmerge. Anda dapat menggunakan @mergelimit untuk membatasi batas penggabungan sel secara vertikal.
var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
MiniExcel.MergeSameCells(mergedFilePath, path);
csharp
var memoryStream = new MemoryStream();var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
memoryStream.MergeSameCells(path);
Isi file sebelum dan sesudah penggabungan:Tanpa batas penggabungan:


Dengan batas penggabungan:


#### 13. Lewati nilai null
Opsi eksplisit baru untuk menulis sel kosong untuk nilai null:
csharp
DataTable dt = new DataTable();/ ... /
DataRow dr = dt.NewRow();
dr["Name1"] = "Somebody once"; dr["Name2"] = null; dr["Name3"] = "told me.";
dt.Rows.Add(dr);
OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = true // Default value. };
MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);

xml
Perilaku sebelumnya:
csharp
/ ... /OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = false // Default value is true. };
MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);

xml
Berfungsi untuk nilai null dan DBNull.#### 14. Membekukan Panel
csharp
/ ... /OpenXmlConfiguration configuration = new OpenXmlConfiguration() { FreezeRowCount = 1, // default is 1 FreezeColumnCount = 2 // default is 0 };
MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);
csharp // 1. By POCO var value = new { Name = "Jack", CreateDate = new DateTime(2021, 01, 01), VIP = true, Points = 123 }; MiniExcel.SaveAsByTemplate(path, templatePath, value);{{nama variabel}}Isi Data ke Template Excel
- Deklarasinya mirip dengan template Vue
, atau perenderan koleksi{{nama koleksi.nama field}}Perenderan koleksi mendukung IEnumerable/DataTable/DapperRow #### 1. Isi Dasar
Template:
Hasil:
Kode:
// 2. By Dictionary
var value = new Dictionary#### 2. Pengisian Data IEnumerable
Catatan1: Gunakan IEnumerable pertama dari kolom yang sama sebagai dasar untuk mengisi daftar
Template:

Hasil:

Kode:
csharp //1. By POCO var value = new { employees = new[] { new {name="Jack",department="HR"}, new {name="Lisa",department="HR"}, new {name="John",department="HR"}, new {name="Mike",department="IT"}, new {name="Neo",department="IT"}, new {name="Loan",department="IT"} } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);
//2. By Dictionary
var value = new Dictionary#### 3. Pengisian Data Kompleks
Catatan: Mendukung multi-sheet dan menggunakan variabel yang sama
Template:

Hasil:

csharp // 1. By POCO var value = new { title = "FooCompany", managers = new[] { new {name="Jack",department="HR"}, new {name="Loan",department="IT"} }, employees = new[] { new {name="Wade",department="HR"}, new {name="Felix",department="HR"}, new {name="Eric",department="IT"}, new {name="Keaton",department="IT"} } }; MiniExcel.SaveAsByTemplate(path, templatePath, value);
// 2. By Dictionary
var value = new Dictionary#### 4. Isi Performa Big Data
CATATAN: Menggunakan eksekusi tertunda IEnumerable bukan ToList dapat menghemat penggunaan memori maksimum di MiniExcel

#### 5. Tipe pemetaan otomatis nilai sel
Template

Hasil

Kelas
csharp public class Poco { public string @string { get; set; } public int? @int { get; set; } public decimal? @decimal { get; set; } public double? @double { get; set; } public DateTime? datetime { get; set; } public bool? @bool { get; set; } public Guid? Guid { get; set; } }
Kodecsharp
var poco = new TestIEnumerableTypePoco { @string = "string", @int = 123, @decimal = decimal.Parse("123.45"), @double = (double)123.33, @datetime = new DateTime(2021, 4, 1), @bool = true, @Guid = Guid.NewGuid() };
var value = new
{
Ts = new[] {
poco,
new TestIEnumerableTypePoco{},
null,
poco
}
};
MiniExcel.SaveAsByTemplate(path, templatePath, value);
#### 6. Contoh : Daftar Proyek GithubTemplate

Hasil

Kode
csharp
var projects = new[]
{
new {Name = "MiniExcel",Link="https://github.com/mini-software/MiniExcel",Star=146, CreateTime=new DateTime(2021,03,01)},
new {Name = "HtmlTableHelper",Link="https://github.com/mini-software/HtmlTableHelper",Star=16, CreateTime=new DateTime(2020,02,01)},
new {Name = "PocoClassGenerator",Link="https://github.com/mini-software/PocoClassGenerator",Star=16, CreateTime=new DateTime(2019,03,17)}
};
var value = new
{
User = "ITWeiHan",
Projects = projects,
TotalStar = projects.Sum(s => s.Star)
};
MiniExcel.SaveAsByTemplate(path, templatePath, value);
#### 7. Pengisian Data Berkelompokcsharp
var value = new Dictionarycsharp @if(name == Jack) {{employees.name}} @elseif(name == Neo) Test {{employees.name}} @else {{employees.department}} @endif##### 1. Dengan tag@groupdan dengan tag@headerSebelum
Sesudah
##### 2. Dengan tag @group dan tanpa tag @header
Sebelum
Sesudah
##### 3. Tanpa tag @group
Sebelum
Sesudah
#### 8. Pernyataan If/ElseIf/Else di dalam sel
Aturan:
- Mendukung DateTime, Double, Int dengan operator ==, !=, >, >=, <, <=.
- Mendukung String dengan operator ==, !=.
- Setiap pernyataan harus baris baru.
- Satu spasi harus ditambahkan sebelum dan sesudah operator.
- Tidak boleh ada baris baru di dalam pernyataan.
- Sel harus dalam format persis seperti di bawah ini.
Sebelum
Sesudah

#### 9. DataTable sebagai parameter
csharp
var managers = new DataTable();
{
managers.Columns.Add("name");
managers.Columns.Add("department");
managers.Rows.Add("Jack", "HR");
managers.Rows.Add("Loan", "IT");
}
var value = new Dictionarycsharp var config = new OpenXmlConfiguration() { IgnoreTemplateParameterMissing = false, }; MiniExcel.SaveAsByTemplate(path, templatePath, value, config)#### 10. Rumus$##### 1. Contoh Awali rumus Anda dengan
dan gunakan$enumrowstartserta$enumrowenduntuk menandai referensi ke baris awal dan akhir enumerasi:$
Saat template dirender, awalan
akan dihapus dan$enumrowstartserta$enumrowendakan digantikan dengan nomor baris awal dan akhir enumerasi:$=SUM(C{{$enumrowstart}}:C{{$enumrowend}})
##### 2. Contoh Rumus Lainnya:
| | | |--------------|-------------------------------------------------------------------------------------------| | Jumlah |
| | Alt. Rata-rata |$=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}})| | Rentang |$=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}})|IgnoreTemplateParameterMissing#### 11. Lain-lain
##### 1. Memeriksa kunci parameter template
Sejak V1.24.0, secara default mengabaikan kunci parameter template yang hilang dan menggantinya dengan string kosong,
dapat mengontrol apakah akan melempar exception atau tidak.

Atribut Nama Kolom/Indeks/Abaikan pada Excel
#### 1. Menentukan nama kolom, indeks kolom, abaikan kolom
Contoh Excel

Kode
csharp
public class ExcelAttributeDemo
{
[ExcelColumnName("Column1")]
public string Test1 { get; set; }
[ExcelColumnName("Column2")]
public string Test2 { get; set; }
[ExcelIgnore]
public string Test3 { get; set; }
[ExcelColumnIndex("I")] // system will convert "I" to 8 index
public string Test4 { get; set; }
public string Test5 { get; } //wihout set will ignore
public string Test6 { get; private set; } //un-public set will ignore
[ExcelColumnIndex(3)] // start with 0
public string Test7 { get; set; }
}var rows = MiniExcel.Query
#### 2. Format Kustom (ExcelFormatAttribute)
Sejak V0.21.0 mendukung kelas yang berisi metode format ToString(string content)
Kelas
csharp public class Dto { public string Name { get; set; }
[ExcelFormat("MMMM dd, yyyy")] public DateTime InDate { get; set; } }
Kodecsharp
var value = new Dto[] {
new Issue241Dto{ Name="Jack",InDate=new DateTime(2021,01,04)},
new Issue241Dto{ Name="Henry",InDate=new DateTime(2020,04,05)},
};
MiniExcel.SaveAs(path, value);
Hasil
Query mendukung konversi format khusus

#### 3. Atur Lebar Kolom (ExcelColumnWidthAttribute)
csharp
public class Dto
{
[ExcelColumnWidth(20)]
public int ID { get; set; }
[ExcelColumnWidth(15.50)]
public string Name { get; set; }
}
#### 4. Beberapa nama kolom dipetakan ke properti yang sama.csharp
public class Dto
{
[ExcelColumnName(excelColumnName:"EmployeeNo",aliases:new[] { "EmpNo","No" })]
public string Empno { get; set; }
public string Name { get; set; }
}
#### 5. System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttributeSejak 1.24.0, sistem mendukung System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute
C#
public class TestIssueI4TXGTDto
{
public int ID { get; set; }
public string Name { get; set; }
[DisplayName("Specification")]
public string Spc { get; set; }
[DisplayName("Unit Price")]
public decimal Up { get; set; }
}
#### 6. ExcelColumnAttributeSejak V1.26.0, beberapa atribut dapat disederhanakan seperti :
csharp
public class TestIssueI4ZYUUDto
{
[ExcelColumn(Name = "ID",Index =0)]
public string MyProperty { get; set; }
[ExcelColumn(Name = "CreateDate", Index = 1,Format ="yyyy-MM",Width =100)]
public DateTime MyProperty2 { get; set; }
}
#### 7. DynamicColumnAttributeSejak V1.26.0, kita dapat mengatur atribut Column secara dinamis
csharp
var config = new OpenXmlConfiguration
{
DynamicColumns = new DynamicExcelColumn[] {
new DynamicExcelColumn("id"){Ignore=true},
new DynamicExcelColumn("name"){Index=1,Width=10},
new DynamicExcelColumn("createdate"){Index=0,Format="yyyy-MM-dd",Width=15},
new DynamicExcelColumn("point"){Index=2,Name="Account Point"},
}
};
var path = PathHelper.GetTempPath();
var value = new[] { new { id = 1, name = "Jack", createdate = new DateTime(2022, 04, 12) ,point = 123.456} };
MiniExcel.SaveAs(path, value, configuration: config);

#### 8. DynamicSheetAttribute
Sejak V1.31.4 kita dapat mengatur atribut Sheet secara dinamis. Kita dapat mengatur nama dan status (visibilitas) sheet.
csharp
var configuration = new OpenXmlConfiguration
{
DynamicSheets = new DynamicExcelSheet[] {
new DynamicExcelSheet("usersSheet") { Name = "Users", State = SheetState.Visible },
new DynamicExcelSheet("departmentSheet") { Name = "Departments", State = SheetState.Hidden }
}
}; var users = new[] { new { Name = "Jack", Age = 25 }, new { Name = "Mike", Age = 44 } };
var department = new[] { new { ID = "01", Name = "HR" }, new { ID = "02", Name = "IT" } };
var sheets = new Dictionary
var path = PathHelper.GetTempPath(); MiniExcel.SaveAs(path, sheets, configuration: configuration);
Kita juga dapat menggunakan atribut baru ExcelSheetAttribute:C#
[ExcelSheet(Name = "Departments", State = SheetState.Hidden)]
private class DepartmentDto
{
[ExcelColumn(Name = "ID",Index = 0)]
public string ID { get; set; }
[ExcelColumn(Name = "Name",Index = 1)]
public string Name { get; set; }
}
### Tambah, Hapus, Perbarui#### Tambah
v1.28.0 mendukung penyisipan data N baris CSV setelah baris terakhir
csharp
// Origin
{
var value = new[] {
new { ID=1,Name ="Jack",InDate=new DateTime(2021,01,03)},
new { ID=2,Name ="Henry",InDate=new DateTime(2020,05,03)},
};
MiniExcel.SaveAs(path, value);
}
// Insert 1 rows after last
{
var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) };
MiniExcel.Insert(path, value);
}
// Insert N rows after last
{
var value = new[] {
new { ID=4,Name ="Frank",InDate=new DateTime(2021,06,07)},
new { ID=5,Name ="Gloria",InDate=new DateTime(2022,05,03)},
};
MiniExcel.Insert(path, value);
}

v1.37.0 mendukung excel untuk menyisipkan lembar baru ke dalam workbook yang sudah ada
csharp
// Origin excel
{
var value = new[] {
new { ID=1,Name ="Jack",InDate=new DateTime(2021,01,03)},
new { ID=2,Name ="Henry",InDate=new DateTime(2020,05,03)},
};
MiniExcel.SaveAs(path, value, sheetName: "Sheet1");
}
// Insert a new sheet
{
var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) };
MiniExcel.Insert(path, table, sheetName: "Sheet2");
}
csharp stream.SaveAs(excelType:ExcelType.CSV); //or stream.SaveAs(excelType:ExcelType.XLSX); //or stream.Query(excelType:ExcelType.CSV); //or stream.Query(excelType:ExcelType.XLSX);#### Hapus(menunggu)ekstensi file#### Perbarui(menunggu)
Pemeriksaan Otomatis Tipe Excel
- MiniExcel akan memeriksa apakah file tersebut xlsx atau csv berdasarkan
secara default, namun mungkin terjadi ketidakakuratan, silakan tentukan secara manual.Stream tidak dapat diketahui berasal dari excel yang mana, silakan tentukan secara manual.
csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { Seperator=';' }; MiniExcel.SaveAs(path, values,configuration: config);stringCSV
#### Catatan
- Secara default mengembalikan tipe
, dan nilai tidak akan diubah menjadi angka atau datetime, kecuali tipe didefinisikan dengan generic tipe kuat.,#### Pemisah khusus
Secara default adalah
sebagai pemisah, Anda dapat mengubah propertiSeperatoruntuk kustomisasi
Sejak V1.30.1 mendukung fungsi untuk kustomisasi pemisah (terima kasih @hyzx86)csharp
var config = new CsvConfiguration()
{
SplitFn = (row) => Regex.Split(row, $"\"" target="_blank" rel="noopener noreferrer">\t,$)")
.Select(s => Regex.Replace(s.Replace("\"\"", "\""), "^\"|\"$", "")).ToArray()
};
var rows = MiniExcel.Query(path, configuration: config).ToList();
csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { NewLine='\n' }; MiniExcel.SaveAs(path, values,configuration: config);#### Pemisah baris kustom\r\nDefault-nya adalah
sebagai karakter baris baru, Anda dapat memodifikasi propertiNewLineuntuk penyesuaian
#### Pengkodean khusus- Pengkodean default adalah "Deteksi Pengkodean Dari Tanda Urutan Byte" (detectEncodingFromByteOrderMarks: true)
- Jika Anda memiliki kebutuhan pengkodean khusus, silakan modifikasi properti StreamReaderFunc / StreamWriterFunc
csharp
// Read
var config = new MiniExcelLibs.Csv.CsvConfiguration()
{
StreamReaderFunc = (stream) => new StreamReader(stream,Encoding.GetEncoding("gb2312"))
};
var rows = MiniExcel.Query(path, true,excelType:ExcelType.CSV,configuration: config);// Write var config = new MiniExcelLibs.Csv.CsvConfiguration() { StreamWriterFunc = (stream) => new StreamWriter(stream, Encoding.GetEncoding("gb2312")) }; MiniExcel.SaveAs(path, value,excelType:ExcelType.CSV, configuration: config);
#### Baca string kosong sebagai nullSecara default, nilai kosong dipetakan ke string.Empty. Anda dapat mengubah perilaku ini
csharp
var config = new MiniExcelLibs.Csv.CsvConfiguration()
{
ReadEmptyStringAsNull = true
};
### DataReader#### 1. GetReader
Sejak 1.23.0, Anda dapat menggunakan GetDataReader
csharp
using (var reader = MiniExcel.GetReader(path,true))
{
while (reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
var value = reader.GetValue(i);
}
}
}
Async
- v0.17.0 mendukung Async (terima kasih kepada isdaniel ( SHIH,BING-SIOU)](https://github.com/isdaniel))
csharp
public static Task SaveAsAsync(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null)
public static Task SaveAsAsync(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null)
public static Taskcsharp public class Dto { public string Name { get; set; } public I49RYZUserType UserType { get; set; } }- v1.25.0 mendukungcancellationToken。Lainnya
#### 1. Enum
Pastikan nama excel & properti sama, sistem akan otomatis memetakan (tidak sensitif huruf besar/kecil)
Sejak V0.18.0 mendukung Enum Description
public enum Type { [Description("General User")] V1, [Description("General Administrator")] V2, [Description("Super Administrator")] V3 }

Sejak versi 1.30.0 mendukung Deskripsi Excel ke Enum, terima kasih kepada @KaneLeung
#### 2. Konversi CSV ke XLSX atau Konversi XLSX ke CSV
csharp
MiniExcel.ConvertXlsxToCsv(xlsxPath, csvPath);
MiniExcel.ConvertXlsxToCsv(xlsxStream, csvStream);
MiniExcel.ConvertCsvToXlsx(csvPath, xlsxPath);
MiniExcel.ConvertCsvToXlsx(csvStream, xlsxStream);
#### 3. CultureInfo Kustom`csharp using (var excelStream = new FileStream(path: filePath, FileMode.Open, FileAccess.Read)) using (var csvStream = new MemoryStream()) { MiniExcel.ConvertXlsxToCsv(excelStream, csvStream); }
Sejak 1.22.0, Anda dapat mengkustomisasi CultureInfo seperti di bawah ini, default sistem adalah CultureInfo.InvariantCulture.
var config = new CsvConfiguration()
{
Culture = new CultureInfo("fr-FR"),
};
MiniExcel.SaveAs(path, value, configuration: config);// or
MiniExcel.Query(path, configuration: config);
#### 4. Ukuran Buffer Kustom public abstract class Configuration : IConfiguration
{
public int BufferSize { get; set; } = 1024 * 512;
}
#### 5. FastModeSistem tidak akan mengontrol memori, tetapi Anda dapat memperoleh kecepatan penyimpanan yang lebih cepat.
var config = new OpenXmlConfiguration() { FastMode = true };
MiniExcel.SaveAs(path, reader,configuration:config);
#### 6. Batch Tambah Gambar (MiniExcel.AddPicture)Silakan tambahkan gambar sebelum melakukan batch generate data baris, atau sistem akan menggunakan memori besar saat memanggil AddPicture.
var images = new[]
{
new MiniExcelPicture
{
ImageBytes = File.ReadAllBytes(PathHelper.GetFile("images/github_logo.png")),
SheetName = null, // default null is first sheet
CellAddress = "C3", // required
},
new MiniExcelPicture
{
ImageBytes = File.ReadAllBytes(PathHelper.GetFile("images/google_logo.png")),
PictureType = "image/png", // default PictureType = image/png
SheetName = "Demo",
CellAddress = "C9", // required
WidthPx = 100,
HeightPx = 100,
},
};
MiniExcel.AddPicture(path, images);
#### 7. Dapatkan Dimensi Lembar
var dim = MiniExcel.GetSheetDimensions(path);
Contoh:
#### 1. SQLite & Dapper File Berukuran Besar SQL Insert Menghindari OOM
catatan : mohon jangan memanggil metode ToList/ToArray setelah Query, karena akan memuat semua data ke dalam memori
using (var connection = new SQLiteConnection(connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
using (var stream = File.OpenRead(path))
{
var rows = stream.Query();
foreach (var row in rows)
connection.Execute("insert into T (A,B) values (@A,@B)", new { row.A, row.B }, transaction: transaction);
transaction.Commit();
}
}
performa:

#### 2. Demo API Download/Upload Excel Xlsx ASP.NET Core 3.1 atau MVC 5 Coba di sini
public class ApiController : Controller
{
public IActionResult Index()
{
return new ContentResult
{
ContentType = "text/html",
StatusCode = (int)HttpStatusCode.OK,
Content = @"
DownloadExcel
DownloadExcelFromTemplatePath
DownloadExcelFromTemplateBytes
Upload Excel
public IActionResult DownloadExcel()
{
var values = new[] {
new { Column1 = "MiniExcel", Column2 = 1 },
new { Column1 = "Github", Column2 = 2}
};
var memoryStream = new MemoryStream();
memoryStream.SaveAs(values);
memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = "demo.xlsx"
};
}
public IActionResult DownloadExcelFromTemplatePath()
{
string templatePath = "TestTemplateComplex.xlsx";
Dictionary value = new Dictionary()
{
["title"] = "FooCompany",
["managers"] = new[] {
new {name="Jack",department="HR"},
new {name="Loan",department="IT"}
},
["employees"] = new[] {
new {name="Wade",department="HR"},
new {name="Felix",department="HR"},
new {name="Eric",department="IT"},
new {name="Keaton",department="IT"}
}
};
MemoryStream memoryStream = new MemoryStream();
memoryStream.SaveAsByTemplate(templatePath, value);
memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = "demo.xlsx"
};
}
private static Dictionary TemplateBytesCache = new Dictionary();
static ApiController()
{
string templatePath = "TestTemplateComplex.xlsx";
byte[] bytes = System.IO.File.ReadAllBytes(templatePath);
TemplateBytesCache.Add(templatePath, bytes);
}
public IActionResult DownloadExcelFromTemplateBytes()
{
byte[] bytes = TemplateBytesCache["TestTemplateComplex.xlsx"];
Dictionary value = new Dictionary()
{
["title"] = "FooCompany",
["managers"] = new[] {
new {name="Jack",department="HR"},
new {name="Loan",department="IT"}
},
["employees"] = new[] {
new {name="Wade",department="HR"},
new {name="Felix",department="HR"},
new {name="Eric",department="IT"},
new {name="Keaton",department="IT"}
}
};
MemoryStream memoryStream = new MemoryStream();
memoryStream.SaveAsByTemplate(bytes, value);
memoryStream.Seek(0, SeekOrigin.Begin);
return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
{
FileDownloadName = "demo.xlsx"
};
}
public IActionResult UploadExcel(IFormFile excel)
{
var stream = new MemoryStream();
excel.CopyTo(stream);
foreach (var item in stream.Query(true))
{
// do your logic etc.
}
return Ok("File uploaded successfully");
}
}
#### 3. Kueri Pagingvoid Main()
{
var rows = MiniExcel.Query(path); Console.WriteLine("==== No.1 Page ====");
Console.WriteLine(Page(rows,pageSize:3,page:1));
Console.WriteLine("==== No.50 Page ====");
Console.WriteLine(Page(rows,pageSize:3,page:50));
Console.WriteLine("==== No.5000 Page ====");
Console.WriteLine(Page(rows,pageSize:3,page:5000));
}
public static IEnumerable Page(IEnumerable en, int pageSize, int page)
{
return en.Skip(page * pageSize).Take(pageSize);
}

#### 4. WebForm ekspor Excel menggunakan memorystream
var fileName = "Demo.xlsx";
var sheetName = "Sheet1";
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.AddHeader("Content-Disposition", $"attachment;filename=\"{fileName}\"");
var values = new[] {
new { Column1 = "MiniExcel", Column2 = 1 },
new { Column1 = "Github", Column2 = 2}
};
var memoryStream = new MemoryStream();
memoryStream.SaveAs(values, sheetName: sheetName);
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.CopyTo(Response.OutputStream);
response.End();
#### 5. Manajemen otoritas peran dan multi-bahasa i18n dinamisSeperti pada contoh, buatlah sebuah metode untuk menangani i18n dan manajemen izin, dan gunakan yield return untuk mengembalikan IEnumerable agar mendapatkan efek pemrosesan yang dinamis dan hemat memori
void Main()
{
var value = new Order[] {
new Order(){OrderNo = "SO01",CustomerID="C001",ProductID="P001",Qty=100,Amt=500},
new Order(){OrderNo = "SO02",CustomerID="C002",ProductID="P002",Qty=300,Amt=400},
}; Console.WriteLine("en-Us and Sales role");
{
var path = Path.GetTempPath() + Guid.NewGuid() + ".xlsx";
var lang = "en-US";
var role = "Sales";
MiniExcel.SaveAs(path, GetOrders(lang, role, value));
MiniExcel.Query(path, true).Dump();
}
Console.WriteLine("zh-CN and PMC role");
{
var path = Path.GetTempPath() + Guid.NewGuid() + ".xlsx";
var lang = "zh-CN";
var role = "PMC";
MiniExcel.SaveAs(path, GetOrders(lang, role, value));
MiniExcel.Query(path, true).Dump();
}
}
private IEnumerable> GetOrders(string lang, string role, Order[] orders)
{
foreach (var order in orders)
{
var newOrder = new Dictionary();
if (lang == "zh-CN")
{
newOrder.Add("客户编号", order.CustomerID);
newOrder.Add("订单编号", order.OrderNo);
newOrder.Add("产品编号", order.ProductID);
newOrder.Add("数量", order.Qty);
if (role == "Sales")
newOrder.Add("价格", order.Amt);
yield return newOrder;
}
else if (lang == "en-US")
{
newOrder.Add("Customer ID", order.CustomerID);
newOrder.Add("Order No", order.OrderNo);
newOrder.Add("Product ID", order.ProductID);
newOrder.Add("Quantity", order.Qty);
if (role == "Sales")
newOrder.Add("Amount", order.Amt);
yield return newOrder;
}
else
{
throw new InvalidDataException($"lang {lang} wrong");
}
}
}
public class Order
{
public string OrderNo { get; set; }
public string CustomerID { get; set; }
public decimal Qty { get; set; }
public string ProductID { get; set; }
public decimal Amt { get; set; }
}

FAQ
#### Q: Judul header Excel tidak sama dengan nama properti kelas, bagaimana cara memetakan?
A. Silakan gunakan atribut ExcelColumnName

#### Q. Bagaimana cara melakukan query atau ekspor multi-sheets?
A. Gunakan metode GetSheetNames dengan parameter sheetName pada Query.
var sheets = MiniExcel.GetSheetNames(path);
foreach (var sheet in sheets)
{
Console.WriteLine($"sheet name : {sheet} ");
var rows = MiniExcel.Query(path,useHeaderRow:true,sheetName:sheet);
Console.WriteLine(rows);
}

#### Q. Bagaimana cara menanyakan atau mengekspor informasi tentang visibilitas sheet?
A. Metode GetSheetInformations.
var sheets = MiniExcel.GetSheetInformations(path);
foreach (var sheetInfo in sheets)
{
Console.WriteLine($"sheet index : {sheetInfo.Index} "); // next sheet index - numbered from 0
Console.WriteLine($"sheet name : {sheetInfo.Name} "); // sheet name
Console.WriteLine($"sheet state : {sheetInfo.State} "); // sheet visibility state - visible / hidden
}
#### Q. Apakah penggunaan Count akan memuat semua data ke dalam memori?Tidak, pengujian gambar memiliki 1 juta baris*10 kolom data, penggunaan memori maksimum adalah <60MB, dan memerlukan waktu 13,65 detik

#### Q. Bagaimana Query menggunakan indeks integer?
Indeks default dari Query adalah string Key: A,B,C.... Jika Anda ingin mengubah ke indeks numerik, silakan buat metode berikut untuk mengonversi
void Main()
{
var path = @"D:\git\MiniExcel\samples\xlsx\TestTypeMapping.xlsx";
var rows = MiniExcel.Query(path,true);
foreach (var r in ConvertToIntIndexRows(rows))
{
Console.Write($"column 0 : {r[0]} ,column 1 : {r[1]}");
Console.WriteLine();
}
}private IEnumerable> ConvertToIntIndexRows(IEnumerable
var dic = new Dictionary();
var index = 0;
foreach (var key in keys)
dic[index++] = r[key];
yield return dic;
}
}
#### Q. Tidak ada judul, Excel kosong dihasilkan ketika nilai kosong saat mengekspor ExcelKarena MiniExcel menggunakan logika yang mirip dengan JSON.NET untuk secara dinamis mendapatkan tipe dari nilai guna menyederhanakan operasi API, tipe tidak dapat diketahui tanpa data. Anda dapat melihat issue #133 untuk pemahaman.

Tipe kuat & DataTable akan menghasilkan header, tetapi Dictionary tetap menghasilkan Excel kosong
#### Q. Bagaimana cara menghentikan foreach ketika baris kosong?
MiniExcel dapat digunakan dengan LINQ TakeWhile untuk menghentikan iterator foreach.

#### Q. Bagaimana cara menghapus baris kosong?

IEnumerable :
public static IEnumerable QueryWithoutEmptyRow(Stream stream, bool useHeaderRow, string sheetName, ExcelType excelType, string startCell, IConfiguration configuration)
{
var rows = stream.Query(useHeaderRow,sheetName,excelType,startCell,configuration);
foreach (IDictionary row in rows)
{
if(row.Keys.Any(key=>row[key]!=null))
yield return row;
}
} DataTable :
public static DataTable QueryAsDataTableWithoutEmptyRow(Stream stream, bool useHeaderRow, string sheetName, ExcelType excelType, string startCell, IConfiguration configuration)
{
if (sheetName == null && excelType != ExcelType.CSV) /Issue #279/
sheetName = stream.GetSheetNames().First(); var dt = new DataTable(sheetName);
var first = true;
var rows = stream.Query(useHeaderRow,sheetName,excelType,startCell,configuration);
foreach (IDictionary row in rows)
{
if (first)
{
foreach (var key in row.Keys)
{
var column = new DataColumn(key, typeof(object)) { Caption = key };
dt.Columns.Add(column);
}
dt.BeginLoadData();
first = false;
}
var newRow = dt.NewRow();
var isNull=true;
foreach (var key in row.Keys)
{
var _v = row[key];
if(_v!=null)
isNull = false;
newRow[key] = _v;
}
if(!isNull)
dt.Rows.Add(newRow);
}
dt.EndLoadData();
return dt;
}
#### Q. Bagaimana SaveAs(path,value) untuk mengganti file yang sudah ada dan tanpa menampilkan error "The file ...xlsx already exists"
Silakan gunakan kelas Stream untuk menyesuaikan logika pembuatan file, misalnya:
`C#
using (var stream = File.Create("Demo.xlsx"))
MiniExcel.SaveAs(stream,value);
atau, sejak V1.25.0, SaveAs mendukung parameter overwriteFile untuk mengaktifkan/nonaktifkan penimpaan file yang sudah ada
csharp MiniExcel.SaveAs(path, value, overwriteFile: true); ``
Keterbatasan dan peringatan
- Saat ini tidak mendukung xls dan file terenkripsi
- xlsm hanya mendukung Query
Referensi
ExcelDataReader / ClosedXML / Dapper / ExcelNumberFormat
Terima kasih
#### Jetbrains

Terima kasih telah menyediakan All product IDE secara gratis untuk proyek ini (Lisensi)
Donasi berbagi kontribusi
Tautan https://github.com/orgs/mini-software/discussions/754Kontributor
--- Tranlated By Open Ai Tx | Last indexed: 2025-10-09 ---










