Questo progetto fa parte della .NET Foundation e opera secondo il loro codice di condotta.
English | 简体中文 | 繁體中文 | 日本語 | 한국어 | हिन्दी | ไทย | Français | Deutsch | Español | Italiano | Русский | Português | Nederlands | Polski | العربية | فارسی | Türkçe | Tiếng Việt | Bahasa Indonesia
Introduzione
MiniExcel è uno strumento semplice ed efficiente per la gestione di file Excel su .NET, progettato specificamente per minimizzare l’utilizzo della memoria.
Attualmente, la maggior parte dei framework popolari necessita di caricare tutti i dati da un documento Excel in memoria per facilitarne le operazioni, ma questo può causare problemi di consumo di memoria. L’approccio di MiniExcel è diverso: i dati vengono elaborati riga per riga in modalità streaming, riducendo il consumo originale da centinaia di megabyte a pochi megabyte, prevenendo efficacemente i problemi di esaurimento memoria (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;
Funzionalità
- Minimizza il consumo di memoria, prevenendo errori di memoria insufficiente (OOM) ed evitando garbage collection complete
- Abilita operazioni in tempo reale a livello di riga per migliori prestazioni su grandi set di dati
- Supporta LINQ con esecuzione differita, permettendo paginazione veloce ed efficiente in termini di memoria e query complesse
- Leggero, senza necessità di Microsoft Office o componenti COM+, e una DLL inferiore a 500KB
- API semplice e intuitiva per leggere/scrivere/riempire excel
Anteprima versione 2.0
Stiamo lavorando su una futura versione di MiniExcel, con una nuova API modulare e focalizzata,
pacchetti nuget separati per le funzionalità Core e Csv, pieno supporto per query asincrone tramite IAsyncEnumerable,
e altre novità in arrivo! I pacchetti saranno disponibili in pre-release, quindi sentitevi liberi di provarli e darci un feedback!
Se lo fate, assicuratevi anche di consultare la nuova documentazione e le note di aggiornamento.
Inizia subito
- Importa/Interroga Excel
- Esporta/Crea Excel
- Template Excel
- Nome/Indice/Attributo Ignora Colonna Excel
- Esempi
Installazione
Puoi installare il pacchetto da NuGet
Note di rilascio
Controlla le Note di Rilascio
TODO
Si prega di controllare TODO
Prestazioni
Il codice per i benchmark si trova in MiniExcel.Benchmarks.
Il file utilizzato per testare le prestazioni è Test1,000,000x10.xlsx, un documento da 32MB contenente 1.000.000 righe * 10 colonne le cui celle sono riempite con la stringa "HelloWorld".
Per eseguire tutti i benchmark utilizzare:
dotnet run -project .\benchmarks\MiniExcel.Benchmarks -c Release -f net9.0 -filter * --join
Puoi trovare i risultati dei benchmark per l'ultima release qui.Query/Importazione Excel
#### 1. Esegui una query e mappa i risultati su un IEnumerable fortemente tipizzato [[Provalo]](https://dotnetfiddle.net/w5WD1J)
Si consiglia di utilizzare Stream.Query per una maggiore efficienza.
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. Esegui una query e mappala su una lista di oggetti dinamici senza usare head [[Provalo]](https://dotnetfiddle.net/w5WD1J)
- la chiave dinamica è
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. Esegui una query con la prima riga di intestazione [[Provalo]](https://dotnetfiddle.net/w5WD1J)nota : se ci sono colonne con lo stesso nome viene utilizzata l’ultima a destra
Excel di input :
| Colonna1 | Colonna2 | |------------|----------| | 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. Estensione LINQ per il supporto alle query First/Take/Skip ...ecc.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);
}
Prestazioni tra MiniExcel/ExcelDataReader/ClosedXML/EPPlus

#### 5. Query per nome del foglio
MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");
#### 6. Interrogare tutti i nomi dei fogli e le righevar sheetNames = MiniExcel.GetSheetNames(path);
foreach (var sheetName in sheetNames)
{
var rows = MiniExcel.Query(path, sheetName: sheetName);
}
#### 7. Ottieni Colonnevar columns = MiniExcel.GetColumns(path); // e.g result : ["A","B"...]var cnt = columns.Count; // get column count
#### 8. Query dinamica: cast della riga a 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 restituisce DataTableNon consigliato, perché DataTable caricherà tutti i dati in memoria e perderà la caratteristica di basso consumo di memoria di MiniExcel.
``C#
var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);

#### 10. Specificare la cella da cui iniziare a leggere i dati
csharp MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
csharp var config = new OpenXmlConfiguration() { FillMergedCells = true }; var rows = MiniExcel.Query(path, configuration: config);non utilizzare il riempimento unione#### 11. Compilare Celle Unite
Nota: L'efficienza è più lenta rispetto a
Motivo: Lo standard OpenXml mette mergeCells in fondo al file, il che porta alla necessità di eseguire due foreach sul sheetxml

supporta il riempimento multi-riga e multi-colonna con lunghezza e larghezza variabili

#### 12. Lettura di grandi file tramite cache su disco (Disk-Base Cache - SharedString)
Se la dimensione di SharedStrings supera i 5 MB, MiniExcel utilizzerà di default la cache su disco locale, ad esempio, 10x100000.xlsx (un milione di righe di dati), quando la cache su disco è disabilitata l'utilizzo massimo di memoria è 195MB, ma abilitando la cache su disco servono solo 65MB. Nota, questa ottimizzazione comporta un piccolo costo in termini di efficienza, quindi in questo caso il tempo di lettura aumenta da 7,4 secondi a 27,2 secondi. Se non ne hai bisogno puoi disabilitare la cache su disco con il seguente codice:
csharp
var config = new OpenXmlConfiguration { EnableSharedStringCache = false };
MiniExcel.Query(path,configuration: config)
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);Puoi usareSharedStringCacheSizeper modificare la dimensione del file sharedString oltre la dimensione specificata per la cache su disco


Creare/Esportare Excel
- Deve essere un tipo non astratto con un costruttore pubblico senza parametri.
- MiniExcel supporta l'esecuzione differita tramite IEnumerable; se desideri utilizzare meno memoria, per favore non chiamare metodi come ToList
esempio: uso di memoria con o senza ToList

#### 1. Tipo anonimo o fortemente tipizzato [[Provalo]](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);Crea Risultato File :Raccomandato| Colonna1 | Colonna2 | |-----------|----------| | MiniExcel | 1 | | Github | 2 |
#### 3. IDataReader
, può evitare di caricare tutti i dati in memoria

Esportazione di più fogli con DataReader (consigliato da 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. DatatableNon raccomandato, caricherà tutti i dati in memoriaDataTable usa Caption come primo nome della colonna, poi usa columname
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. Query DapperCommandDefinition + CommandFlags.NoCacheGrazie @shaofing #552, si prega di utilizzare
Il codice seguente caricherà tutti i dati in memoriacsharp
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 su MemoryStream [[Provalo]](https://dotnetfiddle.net/JOen0e)
csharp
using (var stream = new MemoryStream()) //support FileStream,MemoryStream ect.
{
stream.SaveAs(values);
}
ad es.: api per l'esportazione in 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. Creare più fogli
csharp
// 1. Dictionary// 2. DataSet var sheets = new DataSet(); sheets.Add(UsersDataTable); sheets.Add(DepartmentDataTable); //.. MiniExcel.SaveAs(path, sheets);

#### 8. Opzioni TableStyles
Stile predefinito

Senza configurazione dello stile
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. AutoFiltro
Dalla versione v0.19.0
può abilitare/disabilitare l’AutoFiltro, il valore predefinito ètrue, e il modo per impostare AutoFiltro è:
#### 10. Crea Immaginecsharp
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. Esportazione file array di byte
Dalla versione 1.22.0, quando il tipo di valore è
, il sistema salverà per impostazione predefinita il percorso del file nella cella, e durante l'importazione il sistema potrà convertirlo inbyte[]. Se non vuoi utilizzare questa funzione, puoi impostareOpenXmlConfiguration.EnableConvertByteArraysufalse, migliorando così l'efficienza del sistema.byte[]
Dalla versione 1.22.0, quando il tipo di valore è
, il sistema salverà per impostazione predefinita il percorso del file nella cella, e durante l'importazione il sistema potrà convertirlo inbyte[]. Se non vuoi utilizzare questa funzione, puoi impostareOpenXmlConfiguration.EnableConvertByteArraysufalse, migliorando così l'efficienza del sistema.xlsx
#### 12. Unisci le stesse celle verticalmente
Questa funzionalità è supportata solo nel formato
e unisce le celle verticalmente tra i tag @merge e @endmerge. Puoi usare @mergelimit per limitare i confini delle celle da unire verticalmente.
var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
MiniExcel.MergeSameCells(mergedFilePath, path);
csharp
var memoryStream = new MemoryStream();var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
memoryStream.MergeSameCells(path);
Contenuto del file prima e dopo l'unione:Senza limite di unione:


Con limite di unione:


#### 13. Salta i valori null
Nuova opzione esplicita per scrivere celle vuote per i valori 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
Comportamento precedente:csharp
/ ... /OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = false // Default value is true. };
MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);

xml
Funziona per valori null e DBNull.#### 14. Blocca riquadri
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);{{nome variabile}}Compilare i Dati nel Modello Excel
- La dichiarazione è simile al template Vue
, oppure al rendering di una collezione{{nome collezione.nome campo}}Il rendering delle collezioni supporta IEnumerable/DataTable/DapperRow #### 1. Compilazione Base
Modello:
Risultato:
Codice:
// 2. By Dictionary
var value = new Dictionary#### 2. Riempimento Dati IEnumerable
Nota1: Utilizzare il primo IEnumerable della stessa colonna come base per riempire la lista
Template:

Risultato:

Codice:
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. Compilazione Dati Complessi
Nota: Supporta più fogli e utilizzo della stessa variabile
Template:

Risultato:

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. Prestazioni di riempimento Big Data
NOTA: Usare l'esecuzione differita IEnumerable invece di ToList può ridurre al massimo l'uso della memoria in MiniExcel

#### 5. Tipo di mappatura automatica del valore della cella
Template

Risultato

Classe
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; } }
Codicecsharp
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. Esempio : Elencare Progetti GithubModello

Risultato

Codice
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. Riempimento Dati Raggruppaticsharp
var value = new Dictionarycsharp @if(name == Jack) {{employees.name}} @elseif(name == Neo) Test {{employees.name}} @else {{employees.department}} @endif##### 1. Con tag@groupe con tag@headerPrima
Dopo
##### 2. Con tag @group e senza tag @header
Prima
Dopo
##### 3. Senza tag @group
Prima
Dopo
#### 8. Istruzioni If/ElseIf/Else all'interno della cella
Regole:
- Supporta DateTime, Double, Int con operatori ==, !=, >, >=, <, <=.
- Supporta String con operatori ==, !=.
- Ogni istruzione deve essere su una nuova riga.
- Va aggiunto uno spazio singolo prima e dopo gli operatori.
- Non ci devono essere nuove righe all'interno delle istruzioni.
- La cella deve avere esattamente il formato seguente.
Prima
Dopo

#### 9. DataTable come parametro
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. Formule$##### 1. Esempio Anteponi il prefisso
alla tua formula e usa$enumrowstarte$enumrowendper contrassegnare i riferimenti alle righe iniziali e finali enumerabili:$
Quando il template viene renderizzato, il prefisso
verrà rimosso e$enumrowstarte$enumrowendverranno sostituiti con i numeri di riga iniziali e finali dell'enumerabile:$=SUM(C{{$enumrowstart}}:C{{$enumrowend}})
##### 2. Altri esempi di formule:
| | | |--------------|-------------------------------------------------------------------------------------------| | Somma |
| | Media alt. |$=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}})| | Intervallo |$=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}})|IgnoreTemplateParameterMissing#### 11. Altro
##### 1. Verifica della chiave del parametro del template
Dalla versione V1.24.0, per impostazione predefinita viene ignorata la chiave del parametro mancante nel template e sostituita con una stringa vuota,
può controllare se lanciare un'eccezione o meno.

Attributo Nome/Indice/Ignora Colonna di Excel
#### 1. Specificare il nome della colonna, l'indice della colonna, ignorare la colonna
Esempio Excel

Codice
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. Formato Personalizzato (ExcelFormatAttribute)
Dalla versione V0.21.0 è supportata la classe che contiene il metodo di formato ToString(string content)
Classe
csharp public class Dto { public string Name { get; set; }
[ExcelFormat("MMMM dd, yyyy")] public DateTime InDate { get; set; } }
Codicecsharp
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);
Risultato
La query supporta la conversione personalizzata del formato

#### 3. Imposta larghezza colonna (ExcelColumnWidthAttribute)
csharp
public class Dto
{
[ExcelColumnWidth(20)]
public int ID { get; set; }
[ExcelColumnWidth(15.50)]
public string Name { get; set; }
}
#### 4. Più nomi di colonne mappati sulla stessa proprietà.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.excelColumnNameAttributeDalla versione 1.24.0, il sistema supporta 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. ExcelColumnAttributeDalla versione V1.26.0, più attributi possono essere semplificati come segue:
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. DynamicColumnAttributeDalla versione V1.26.0, possiamo impostare dinamicamente gli attributi di Column
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
Dalla versione V1.31.4 è possibile impostare dinamicamente gli attributi del foglio. Possiamo impostare il nome del foglio e lo stato (visibilità).
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);
Possiamo anche utilizzare il nuovo attributo 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; }
}
### Aggiungi, Elimina, Aggiorna#### Aggiungi
v1.28.0 supporta l'inserimento di N righe di dati CSV dopo l'ultima riga
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 supporta l'inserimento di un nuovo foglio Excel in una cartella di lavoro esistente
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);#### Elimina(in attesa)estensione del file#### Aggiorna(in attesa)
Controllo automatico del tipo di Excel
- MiniExcel controllerà se è xlsx o csv in base all'
per impostazione predefinita, ma potrebbero esserci imprecisioni, si prega di specificarlo manualmente.Non è possibile sapere da quale Excel provenga uno stream, si prega di specificarlo manualmente.
csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { Seperator=';' }; MiniExcel.SaveAs(path, values,configuration: config);stringCSV
#### Nota
- Per impostazione predefinita restituisce il tipo
, e il valore non verrà convertito in numeri o datetime, a meno che il tipo non sia definito tramite strong typing generico.,#### Separatore personalizzato
Il valore predefinito è
come separatore, puoi modificare la proprietàSeperatorper la personalizzazione
Dalla versione V1.30.1 supporta la funzione per personalizzare il separatore (grazie a @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);#### Interruzione di riga personalizzata\r\nIl valore predefinito è
come carattere di nuova riga, puoi modificare la proprietàNewLineper la personalizzazione
#### Codifica personalizzata- La codifica predefinita è "Rileva codifica dai contrassegni di ordine dei byte" (detectEncodingFromByteOrderMarks: true)
- Se hai esigenze di codifica personalizzate, modifica la proprietà 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);
#### Leggi stringa vuota come nullPer impostazione predefinita, i valori vuoti vengono mappati su string.Empty. Puoi modificare questo comportamento
csharp
var config = new MiniExcelLibs.Csv.CsvConfiguration()
{
ReadEmptyStringAsNull = true
};
### DataReader#### 1. GetReader
Dalla versione 1.23.0, puoi usare 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 supporta Async (grazie a 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 supportacancellationToken。Altri
#### 1. Enum
Assicurarsi che il nome in excel e nella proprietà sia lo stesso, il sistema eseguirà il mapping automatico (non fa distinzione tra maiuscole e minuscole)
Dalla versione V0.18.0 è supportata la descrizione degli Enum
public enum Type { [Description("General User")] V1, [Description("General Administrator")] V2, [Description("Super Administrator")] V3 }

Dalla versione 1.30.0 è supportata la descrizione Excel per Enum, grazie a @KaneLeung
#### 2. Convertire CSV in XLSX o Convertire XLSX in CSV
csharp
MiniExcel.ConvertXlsxToCsv(xlsxPath, csvPath);
MiniExcel.ConvertXlsxToCsv(xlsxStream, csvStream);
MiniExcel.ConvertCsvToXlsx(csvPath, xlsxPath);
MiniExcel.ConvertCsvToXlsx(csvStream, xlsxStream);
#### 3. CultureInfo personalizzato`csharp using (var excelStream = new FileStream(path: filePath, FileMode.Open, FileAccess.Read)) using (var csvStream = new MemoryStream()) { MiniExcel.ConvertXlsxToCsv(excelStream, csvStream); }
Dalla versione 1.22.0, puoi personalizzare CultureInfo come segue, il valore predefinito di sistema è CultureInfo.InvariantCulture.
var config = new CsvConfiguration()
{
Culture = new CultureInfo("fr-FR"),
};
MiniExcel.SaveAs(path, value, configuration: config);// or
MiniExcel.Query(path, configuration: config);
#### 4. Dimensione Buffer Personalizzata public abstract class Configuration : IConfiguration
{
public int BufferSize { get; set; } = 1024 * 512;
}
#### 5. Modalità VeloceIl sistema non controllerà la memoria, ma puoi ottenere una velocità di salvataggio più elevata.
var config = new OpenXmlConfiguration() { FastMode = true };
MiniExcel.SaveAs(path, reader,configuration:config);
#### 6. Aggiunta Immagine in Batch (MiniExcel.AddPicture)Si prega di aggiungere le immagini prima di generare in batch i dati delle righe, altrimenti il sistema utilizzerà molta memoria quando si chiama 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. Ottieni le dimensioni dei fogli
var dim = MiniExcel.GetSheetDimensions(path);
Esempi:
#### 1. Inserimento SQL di File di Grandi Dimensioni con SQLite & Dapper per Evitare OOM
nota: per favore non chiamare i metodi ToList/ToArray dopo Query, caricheranno tutti i dati in memoria
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();
}
}
prestazioni:

#### 2. ASP.NET Core 3.1 o MVC 5 Download/Upload API Excel Xlsx Demo Provalo
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. Query di Paginazionevoid 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 esporta Excel tramite 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. Gestione dinamica di i18n multilingue e autorità dei ruoliCome nell'esempio, crea un metodo per gestire i18n e la gestione dei permessi, e usa yield return per restituire IEnumerable per ottenere effetti dinamici e di basso consumo di memoria
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
#### D: Il titolo dell'intestazione di Excel non è uguale al nome della proprietà della classe, come mappare?
R. Si prega di utilizzare l'attributo ExcelColumnName

#### D. Come interrogare o esportare più fogli?
R. Metodo GetSheetNames con parametro sheetName di 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);
}

#### D. Come interrogare o esportare informazioni sulla visibilità dei fogli?
R. Metodo 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
}
#### D. L'utilizzo di Count caricherà tutti i dati in memoria?No, il test sull'immagine ha 1 milione di righe * 10 colonne di dati, l'utilizzo massimo della memoria è <60MB e ci vogliono 13,65 secondi

#### D. Come utilizza Query gli indici interi?
L'indice predefinito di Query è la chiave stringa: A,B,C.... Se si desidera passare a un indice numerico, creare il seguente metodo di conversione
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;
}
}
#### D. Nessun titolo, Excel vuoto viene generato quando il valore è vuoto durante l'esportazione in ExcelPoiché MiniExcel utilizza una logica simile a JSON.NET per ottenere dinamicamente il tipo dai valori al fine di semplificare le operazioni API, il tipo non può essere conosciuto senza dati. Puoi consultare issue #133 per ulteriori informazioni.

Tipi forti e DataTable genereranno intestazioni, ma i Dictionary risulteranno comunque Excel vuoti
#### D. Come interrompere il foreach quando c'è una riga vuota?
MiniExcel può essere usato con LINQ TakeWhile per interrompere l'iterazione foreach.

#### D. Come rimuovere le righe vuote?

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;
}
#### D. Come usare SaveAs(path,value) per sostituire un file esistente senza generare l'errore "Il file ...xlsx esiste già"
Si prega di utilizzare la classe Stream per personalizzare la logica di creazione del file, ad esempio:
`C#
using (var stream = File.Create("Demo.xlsx"))
MiniExcel.SaveAs(stream,value);
oppure, dalla versione V1.25.0, SaveAs supporta il parametro overwriteFile per abilitare/disabilitare la sovrascrittura di un file esistente
csharp MiniExcel.SaveAs(path, value, overwriteFile: true); ``
Limitazioni e avvertenze
- Attualmente non supporta file xls e file criptati
- xlsm supporta solo Query
Riferimenti
ExcelDataReader / ClosedXML / Dapper / ExcelNumberFormat
Ringraziamenti
#### Jetbrains

Grazie per aver fornito gratuitamente la licenza All product IDE per questo progetto (Licenza)
Donazioni per la condivisione dei contributi
Link https://github.com/orgs/mini-software/discussions/754Contributori
--- Tranlated By Open Ai Tx | Last indexed: 2025-10-09 ---










