Web Analytics

MiniExcel

⭐ 3234 stars Indonesian by mini-software

NuGet Build status star GitHub stars version Ask DeepWiki


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


Bintang atau Donasi Anda dapat membuat MiniExcel menjadi lebih baik


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

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();

image

#### 2. Jalankan query dan petakan ke daftar objek dinamis tanpa menggunakan head [[Coba di sini]](https://dotnetfiddle.net/w5WD1J)

| MiniExcel | 1 | |-----------|---| | Github | 2 |


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-lain

Query 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 queryfirst

#### 5. Query berdasarkan nama sheet

MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");
#### 6. Query semua nama sheet dan baris

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

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

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

#### 8. Dynamic Query mengubah baris menjadi IDictionary

foreach(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 DataTable

Tidak 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);

image

#### 10. Tentukan sel untuk mulai membaca data

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

#### 11. Isi Sel yang Digabungkan

Catatan: Efisiensinya lebih lambat dibandingkan tidak menggunakan pengisian gabungan

Alasan: Standar OpenXml menempatkan mergeCells di bagian bawah file, sehingga perlu melakukan foreach pada sheetxml dua kali

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

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

image

#### 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)
Anda dapat menggunakan SharedStringCacheSize untuk mengubah ukuran file sharedString melebihi ukuran yang ditentukan untuk caching disk
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);
image

image

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 image

#### 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} });
#### 2. IEnumerable>

csharp var values = new List>() { new Dictionary{{ "Column1", "MiniExcel" }, { "Column2", 1 } }, new Dictionary{{ "Column1", "Github" }, { "Column2", 2 } } }; MiniExcel.SaveAs(path, values);
Buat Hasil File :

| Kolom1 | Kolom2 | |-----------|---------| | MiniExcel | 1 | | Github | 2 |

#### 3. IDataReader

  • Direkomendasikan, dapat menghindari memuat semua data ke dalam memori
csharp MiniExcel.SaveAs(path, reader);
image

DataReader mengekspor beberapa sheet (disarankan oleh Dapper ExecuteReader)

csharp using (var cnn = Connection) { cnn.Open(); var sheets = new Dictionary(); sheets.Add("sheet1", cnn.ExecuteReader("select 1 id")); sheets.Add("sheet2", cnn.ExecuteReader("select 2 id")); MiniExcel.SaveAs("Demo.xlsx", sheets); }
#### 4. Datatable

  • Tidak direkomendasikan, karena akan memuat semua data ke dalam memori
  • DataTable menggunakan Caption untuk nama kolom terlebih dahulu, lalu menggunakan nama kolom
csharp 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); }

MiniExcel.SaveAs(path, table);

#### 5. Dapper Query

Terima kasih @shaofing #552 , silakan gunakan CommandDefinition + CommandFlags.NoCache

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); }
Kode di bawah ini akan memuat semua data ke dalam memori

csharp 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 excel

csharp 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 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 { ["users"] = users, ["department"] = department }; MiniExcel.SaveAs(path, sheets);

// 2. DataSet var sheets = new DataSet(); sheets.Add(UsersDataTable); sheets.Add(DepartmentDataTable); //.. MiniExcel.SaveAs(path, sheets);

image

#### 8. Opsi TableStyles

Gaya bawaan

image

Tanpa konfigurasi gaya

csharp var config = new OpenXmlConfiguration() { TableStyles = TableStyles.None }; MiniExcel.SaveAs(path, value,configuration:config);
image

#### 9. AutoFilter

Sejak v0.19.0 OpenXmlConfiguration.AutoFilter dapat mengaktifkan/nonaktifkan AutoFilter, nilai default adalah true, dan cara mengatur AutoFilter:

csharp MiniExcel.SaveAs(path, value, configuration: new OpenXmlConfiguration() { AutoFilter = false });

#### 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);
image

#### 11. Ekspor File Byte Array

Sejak versi 1.22.0, ketika tipe nilai adalah byte[] maka sistem akan menyimpan path file di sel secara default, dan saat impor sistem dapat mengonversinya menjadi byte[]. Dan jika Anda tidak ingin menggunakannya, Anda dapat mengatur OpenXmlConfiguration.EnableConvertByteArray ke false, ini dapat meningkatkan efisiensi sistem.

image

Sejak versi 1.22.0, ketika tipe nilai adalah byte[] maka sistem akan menyimpan path file di sel secara default, dan saat impor sistem dapat mengonversinya menjadi byte[]. Dan jika Anda tidak ingin menggunakannya, Anda dapat mengatur OpenXmlConfiguration.EnableConvertByteArray ke false, ini dapat meningkatkan efisiensi sistem.

image

#### 12. Menggabungkan sel yang sama secara vertikal

Fungsi ini hanya didukung dalam format xlsx dan menggabungkan sel secara vertikal di antara tag @merge dan @endmerge. Anda dapat menggunakan @mergelimit untuk membatasi batas penggabungan sel secara vertikal.

csharp var mergedFilePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid().ToString()}.xlsx");

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:

Screenshot 2023-08-07 at 11 59 24

Screenshot 2023-08-07 at 11 59 57

Dengan batas penggabungan:

Screenshot 2023-08-08 at 18 21 00

Screenshot 2023-08-08 at 18 21 40

#### 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);

image

xml Somebody once told me.

Perilaku sebelumnya:

csharp / ... /

OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = false // Default value is true. };

MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);

gambar

xml Somebody once told me.
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);

image

Isi Data ke Template Excel

  • Deklarasinya mirip dengan template Vue {{nama variabel}}, atau perenderan koleksi {{nama koleksi.nama field}}
  • Perenderan koleksi mendukung IEnumerable/DataTable/DapperRow
#### 1. Isi Dasar

Template: image

Hasil: image

Kode:

csharp // 1. By POCO var value = new { Name = "Jack", CreateDate = new DateTime(2021, 01, 01), VIP = true, Points = 123 }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

// 2. By Dictionary var value = new Dictionary() { ["Name"] = "Jack", ["CreateDate"] = new DateTime(2021, 01, 01), ["VIP"] = true, ["Points"] = 123 }; MiniExcel.SaveAsByTemplate(path, templatePath, value);

#### 2. Pengisian Data IEnumerable

Catatan1: Gunakan IEnumerable pertama dari kolom yang sama sebagai dasar untuk mengisi daftar

Template: image

Hasil: image

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() { ["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);

#### 3. Pengisian Data Kompleks

Catatan: Mendukung multi-sheet dan menggunakan variabel yang sama

Template:

image

Hasil:

image

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() { ["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);

#### 4. Isi Performa Big Data

CATATAN: Menggunakan eksekusi tertunda IEnumerable bukan ToList dapat menghemat penggunaan memori maksimum di MiniExcel

image

#### 5. Tipe pemetaan otomatis nilai sel

Template

image

Hasil

image

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; } }
Kode

csharp 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 Github

Template

image

Hasil

image

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 Berkelompok

csharp var value = new Dictionary() { ["employees"] = new[] { new {name="Jack",department="HR"}, new {name="Jack",department="HR"}, new {name="John",department="HR"}, new {name="John",department="IT"}, new {name="Neo",department="IT"}, new {name="Loan",department="IT"} } }; await MiniExcel.SaveAsByTemplateAsync(path, templatePath, value);
##### 1. Dengan tag @group dan dengan tag @header

Sebelum

before_with_header

Sesudah

after_with_header

##### 2. Dengan tag @group dan tanpa tag @header

Sebelum

before_without_header

Sesudah

after_without_header

##### 3. Tanpa tag @group

Sebelum

without_group

Sesudah

without_group_after

#### 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.
csharp @if(name == Jack) {{employees.name}} @elseif(name == Neo) Test {{employees.name}} @else {{employees.department}} @endif
Sebelum

if_before

Sesudah

if_after

#### 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 Dictionary() { ["title"] = "FooCompany", ["managers"] = managers, }; MiniExcel.SaveAsByTemplate(path, templatePath, value);
#### 10. Rumus

##### 1. Contoh Awali rumus Anda dengan $ dan gunakan $enumrowstart serta $enumrowend untuk menandai referensi ke baris awal dan akhir enumerasi:

image

Saat template dirender, awalan $ akan dihapus dan $enumrowstart serta $enumrowend akan digantikan dengan nomor baris awal dan akhir enumerasi:

image

##### 2. Contoh Rumus Lainnya:

| | | |--------------|-------------------------------------------------------------------------------------------| | Jumlah | $=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) | | Alt. Rata-rata | $=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}})| | Rentang | $=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}}) |

#### 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, IgnoreTemplateParameterMissing dapat mengontrol apakah akan melempar exception atau tidak.

csharp var config = new OpenXmlConfiguration() { IgnoreTemplateParameterMissing = false, }; MiniExcel.SaveAsByTemplate(path, templatePath, value, config)
image

Atribut Nama Kolom/Indeks/Abaikan pada Excel

#### 1. Menentukan nama kolom, indeks kolom, abaikan kolom

Contoh Excel

image

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(path).ToList(); Assert.Equal("Column1", rows[0].Test1); Assert.Equal("Column2", rows[0].Test2); Assert.Null(rows[0].Test3); Assert.Equal("Test7", rows[0].Test4); Assert.Null(rows[0].Test5); Assert.Null(rows[0].Test6); Assert.Equal("Test4", rows[0].Test7);

#### 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; } }

Kode

csharp 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

image

Query mendukung konversi format khusus

image

#### 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.excelColumnNameAttribute

Sejak 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. ExcelColumnAttribute

Sejak 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. DynamicColumnAttribute

Sejak 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);
image

#### 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 { ["usersSheet"] = users, ["departmentSheet"] = department };

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); }
image

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"); }
#### Hapus(menunggu)

#### Perbarui(menunggu)

Pemeriksaan Otomatis Tipe Excel

  • MiniExcel akan memeriksa apakah file tersebut xlsx atau csv berdasarkan ekstensi file secara default, namun mungkin terjadi ketidakakuratan, silakan tentukan secara manual.
  • Stream tidak dapat diketahui berasal dari excel yang mana, silakan tentukan secara manual.
csharp stream.SaveAs(excelType:ExcelType.CSV); //or stream.SaveAs(excelType:ExcelType.XLSX); //or stream.Query(excelType:ExcelType.CSV); //or stream.Query(excelType:ExcelType.XLSX);

CSV

#### Catatan

  • Secara default mengembalikan tipe string, 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 properti Seperator untuk kustomisasi

csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { Seperator=';' }; MiniExcel.SaveAs(path, values,configuration: config);
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();
#### Pemisah baris kustom

Default-nya adalah \r\n sebagai karakter baris baru, Anda dapat memodifikasi properti NewLine untuk penyesuaian

csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { NewLine='\n' }; MiniExcel.SaveAs(path, values,configuration: config);
#### 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 null

Secara 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 Task> QueryAsync(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) public static Task> QueryAsync(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() public static Task> QueryAsync(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) where T : class, new() public static Task>> QueryAsync(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) public static Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value) public static Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value) public static Task SaveAsByTemplateAsync(string path, string templatePath, object value) public static Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value) public static Task QueryAsDataTableAsync(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null)
-  v1.25.0 mendukung cancellationToken

Lainnya

#### 1. Enum

Pastikan nama excel & properti sama, sistem akan otomatis memetakan (tidak sensitif huruf besar/kecil)

image

Sejak V0.18.0 mendukung Enum Description

csharp public class Dto { public string Name { get; set; } public I49RYZUserType UserType { get; set; } }

public enum Type { [Description("General User")] V1, [Description("General Administrator")] V2, [Description("Super Administrator")] V3 }

image

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);
`csharp
using (var excelStream = new FileStream(path: filePath, FileMode.Open, FileAccess.Read))
using (var csvStream = new MemoryStream())
{
   MiniExcel.ConvertXlsxToCsv(excelStream, csvStream);
}
#### 3. CultureInfo Kustom

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. FastMode

Sistem 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);
Gambar

#### 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: image

#### 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 Paging

void 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); }

20210419

#### 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 dinamis

Seperti 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; } }

image

FAQ

#### Q: Judul header Excel tidak sama dengan nama properti kelas, bagaimana cara memetakan?

A. Silakan gunakan atribut ExcelColumnName

image

#### 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);
}
image

#### 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

image

#### 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 rows) { ICollection keys = null; var isFirst = true; foreach (IDictionary r in rows) { if(isFirst) { keys = r.Keys; isFirst = false; }

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 Excel

Karena 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.

image

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.

Image

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

image

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

jetbrains-variant-2

Terima kasih telah menyediakan All product IDE secara gratis untuk proyek ini (Lisensi)

Donasi berbagi kontribusi

Tautan https://github.com/orgs/mini-software/discussions/754

Kontributor

--- Tranlated By Open Ai Tx | Last indexed: 2025-10-09 ---