Ce projet fait partie de la .NET Foundation et fonctionne selon leur code de conduite.
English | 简体中文 | 繁體中文 | 日本語 | 한국어 | हिन्दी | ไทย | Français | Deutsch | Español | Italiano | Русский | Português | Nederlands | Polski | العربية | فارسی | Türkçe | Tiếng Việt | Bahasa Indonesia
Introduction
MiniExcel est un outil simple et efficace de traitement Excel pour .NET, spécialement conçu pour minimiser l'utilisation de la mémoire.
Actuellement, la plupart des frameworks populaires doivent charger toutes les données d'un document Excel en mémoire pour faciliter les opérations, mais cela peut entraîner des problèmes de consommation de mémoire. L'approche de MiniExcel est différente : les données sont traitées ligne par ligne de manière séquentielle, réduisant la consommation initiale de potentiellement des centaines de mégaoctets à seulement quelques mégaoctets, évitant ainsi efficacement les problèmes de dépassement de mémoire (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;
Fonctionnalités
- Minimise la consommation de mémoire, évitant les erreurs de mémoire insuffisante (OOM) et les collectes de déchets complètes
- Permet des opérations de données en temps réel, au niveau des lignes, pour de meilleures performances sur de grands ensembles de données
- Prend en charge LINQ avec exécution différée, permettant une pagination rapide et économe en mémoire ainsi que des requêtes complexes
- Léger, sans besoin de Microsoft Office ou de composants COM+, et une taille de DLL inférieure à 500 Ko
- API simple et intuitive pour lire/écrire/remplir des fichiers Excel
Version 2.0 aperçu
Nous travaillons sur une future version de MiniExcel, avec une nouvelle API modulaire et ciblée,
des paquets nuget séparés pour les fonctionnalités Core et Csv, une prise en charge complète des requêtes asynchrones diffusées via IAsyncEnumerable,
et bien d'autres nouveautés à venir ! Les paquets seront disponibles en pré-release, alors n'hésitez pas à les découvrir et à nous donner vos retours !
Si vous le faites, pensez également à consulter les nouvelles docs et les notes de mise à niveau.
Pour commencer
- Importer/Interroger Excel
- Exporter/Créer Excel
- Modèle Excel
- Nom de colonne Excel/Index/Attribut Ignorer
- Exemples
Installation
Vous pouvez installer le package depuis NuGet
Notes de version
Veuillez consulter les notes de version
À faire
Veuillez consulter TODO
Performance
Le code des benchmarks se trouve dans MiniExcel.Benchmarks.
Le fichier utilisé pour tester les performances est Test1,000,000x10.xlsx, un document de 32 Mo contenant 1 000 000 lignes * 10 colonnes dont les cellules sont remplies avec la chaîne "HelloWorld".
Pour exécuter tous les benchmarks, utilisez :
dotnet run -project .\benchmarks\MiniExcel.Benchmarks -c Release -f net9.0 -filter * --join
Vous pouvez trouver les résultats des benchmarks pour la dernière version ici.Requête/Importation Excel
#### 1. Exécuter une requête et mapper les résultats vers un IEnumerable fortement typé [[Essayez-le]](https://dotnetfiddle.net/w5WD1J)
Il est recommandé d'utiliser Stream.Query pour une meilleure efficacité.
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. Exécuter une requête et la mapper sur une liste d’objets dynamiques sans utiliser head [[Essayez-le]](https://dotnetfiddle.net/w5WD1J)
- la clé dynamique est
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. Exécuter une requête avec la première ligne comme en-tête [[Essayez-le]](https://dotnetfiddle.net/w5WD1J)note : pour les colonnes de même nom, la dernière à droite est utilisée
Excel d'entrée :
| Colonne1 | Colonne2 | |-----------|----------| | 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. Prise en charge des extensions LINQ pour les requêtes First/Take/Skip ...etcRequête 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);
}
Performance entre MiniExcel/ExcelDataReader/ClosedXML/EPPlus

#### 5. Requête par nom de feuille
MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");
#### 6. Interroger tous les noms de feuilles et les lignesvar sheetNames = MiniExcel.GetSheetNames(path);
foreach (var sheetName in sheetNames)
{
var rows = MiniExcel.Query(path, sheetName: sheetName);
}
#### 7. Obtenir les colonnesvar columns = MiniExcel.GetColumns(path); // e.g result : ["A","B"...]var cnt = columns.Count; // get column count
#### 8. Requête dynamique : conversion d'une ligne en 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. Requête Excel retournant un DataTableNon recommandé, car DataTable chargera toutes les données en mémoire et perdra la caractéristique de faible consommation de mémoire de MiniExcel.
``C#
var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);

#### 10. Spécifiez la cellule à partir de laquelle commencer la lecture des données
csharp MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
csharp var config = new OpenXmlConfiguration() { FillMergedCells = true }; var rows = MiniExcel.Query(path, configuration: config);l'absence de remplissage fusionné#### 11. Remplir les cellules fusionnées
Remarque : L'efficacité est moindre comparée à
Raison : La norme OpenXml place mergeCells en bas du fichier, ce qui oblige à parcourir deux fois le sheetxml

prise en charge du remplissage multi-lignes et colonnes de longueur et largeur variables

#### 12. Lecture de gros fichiers avec cache basé sur disque (Cache basé sur disque - SharedString)
Si la taille des SharedStrings dépasse 5 Mo, MiniExcel utilise par défaut un cache local sur disque, par exemple, 10x100000.xlsx (données d'un million de lignes), lorsque le cache disque est désactivé, l'utilisation maximale de la mémoire est de 195 Mo, mais avec le cache disque activé, seulement 65 Mo sont nécessaires. À noter, cette optimisation entraîne un certain coût en efficacité, donc dans ce cas le temps de lecture passe de 7,4 secondes à 27,2 secondes. Si vous n'en avez pas besoin, vous pouvez désactiver le cache disque avec le code suivant :
csharp
var config = new OpenXmlConfiguration { EnableSharedStringCache = false };
MiniExcel.Query(path,configuration: config)
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);Vous pouvez utiliserSharedStringCacheSizepour modifier la taille du fichier sharedString au-delà de la taille spécifiée pour la mise en cache sur disque.


Créer/Exporter Excel
- Doit être un type non abstrait avec un constructeur public sans paramètre.
- MiniExcel prend en charge l’exécution différée des paramètres IEnumerable. Si vous souhaitez utiliser le moins de mémoire possible, veuillez ne pas appeler de méthodes telles que ToList
ex : ToList ou non consommation de mémoire

#### 1. Anonyme ou typé fortement [[Essayez-le]](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);Créer le résultat du fichier :Recommandé| Colonne1 | Colonne2 | |------------|----------| | MiniExcel | 1 | | Github | 2 |
#### 3. IDataReader
, cela permet d'éviter de charger toutes les données en mémoire

Exportation de plusieurs feuilles avec DataReader (recommandé par 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 recommandé, cela chargera toutes les données en mémoireDataTable utilise d'abord Caption pour le nom de la colonne, puis utilise 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. Requête DapperCommandDefinition + CommandFlags.NoCacheMerci @shaofing #552, veuillez utiliser
Le code ci-dessous chargera toutes les données en mémoirecsharp
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 vers MemoryStream [[Essayer]](https://dotnetfiddle.net/JOen0e)
csharp
using (var stream = new MemoryStream()) //support FileStream,MemoryStream ect.
{
stream.SaveAs(values);
}
par exemple : api d'exportation 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. Créer plusieurs feuilles
csharp
// 1. Dictionary// 2. DataSet var sheets = new DataSet(); sheets.Add(UsersDataTable); sheets.Add(DepartmentDataTable); //.. MiniExcel.SaveAs(path, sheets);

#### 8. Options TableStyles
Style par défaut

Sans configuration de style
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. Filtre automatique
Depuis la version v0.19.0,
peut activer/désactiver le filtre automatique, la valeur par défaut esttrue, et la façon de configurer le filtre automatique est la suivante :
#### 10. Créer une image
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. Exportation de fichiers de tableaux d'octets
Depuis la version 1.22.0, lorsque le type de valeur est
, le système enregistre par défaut le chemin du fichier dans la cellule, et lors de l'importation, le système peut convertir cela enbyte[]. Et si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez définirOpenXmlConfiguration.EnableConvertByteArraysurfalse, cela peut améliorer l'efficacité du système.byte[]
Depuis la version 1.22.0, lorsque le type de valeur est
, le système enregistre par défaut le chemin du fichier dans la cellule, et lors de l'importation, le système peut convertir cela enbyte[]. Et si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez définirOpenXmlConfiguration.EnableConvertByteArraysurfalse, cela peut améliorer l'efficacité du système.xlsx
#### 12. Fusionner les mêmes cellules verticalement
Cette fonctionnalité n'est supportée que dans le format
et fusionne les cellules verticalement entre les balises @merge et @endmerge. Vous pouvez utiliser @mergelimit pour limiter les frontières de la fusion des cellules verticalement.
var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
MiniExcel.MergeSameCells(mergedFilePath, path);
csharp
var memoryStream = new MemoryStream();var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
memoryStream.MergeSameCells(path);
Contenu du fichier avant et après fusion :Sans limite de fusion :


Avec limite de fusion :


#### 13. Ignorer les valeurs nulles
Nouvelle option explicite pour écrire des cellules vides pour les valeurs nulles :
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
Comportement précédent :csharp
/ ... /OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = false // Default value is true. };
MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);

xml
Fonctionne pour les valeurs null et DBNull.#### 14. Figer les volets
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);{{nom de variable}}Remplir des données dans un modèle Excel
- La déclaration est similaire au template Vue
, ou au rendu de collection{{nom de collection.nom du champ}}Le rendu de collection prend en charge IEnumerable/DataTable/DapperRow #### 1. Remplissage de base
Modèle :
Résultat :
Code :
// 2. By Dictionary
var value = new Dictionary#### 2. Remplissage de données IEnumerable
Note1 : Utilisez le premier IEnumerable de la même colonne comme base pour remplir la liste
Modèle :

Résultat :

Code :
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. Remplissage de Données Complexes
Remarque : Prend en charge les multi-feuilles et l’utilisation de la même variable
Modèle :

Résultat :

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. Remplissage des performances Big Data
REMARQUE : Utiliser l'exécution différée IEnumerable au lieu de ToList peut économiser un maximum de mémoire dans MiniExcel

#### 5. Mappage automatique du type de valeur de cellule
Modèle

Résultat

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; } }
Code
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. Exemple : Lister les projets GithubModèle

Résultat

Code
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. Remplissage de données groupéescsharp
var value = new Dictionarycsharp @if(name == Jack) {{employees.name}} @elseif(name == Neo) Test {{employees.name}} @else {{employees.department}} @endif##### 1. Avec la balise@groupet avec la balise@headerAvant
Après
##### 2. Avec la balise @group et sans la balise @header
Avant
Après
##### 3. Sans la balise @group
Avant
Après
#### 8. Instructions If/ElseIf/Else à l'intérieur de la cellule
Règles :
- Prend en charge DateTime, Double, Int avec les opérateurs ==, !=, >, >=, <, <=.
- Prend en charge String avec les opérateurs ==, !=.
- Chaque instruction doit être sur une nouvelle ligne.
- Un espace unique doit être ajouté avant et après les opérateurs.
- Il ne doit pas y avoir de saut de ligne à l'intérieur des instructions.
- La cellule doit être exactement au format ci-dessous.
Avant
Après

#### 9. DataTable comme paramètre
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. Formules$##### 1. Exemple Préfixez votre formule avec
et utilisez$enumrowstartet$enumrowendpour marquer les références au début et à la fin des lignes énumérables :$
Lorsque le modèle est rendu, le préfixe
sera supprimé et$enumrowstartet$enumrowendseront remplacés par les numéros de ligne de début et de fin de l’énumérable :$=SUM(C{{$enumrowstart}}:C{{$enumrowend}})
##### 2. Autres exemples de formules :
| | | |--------------|-------------------------------------------------------------------------------------------| | Somme |
| | Moyenne alt. |$=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}})| | Plage |$=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}})|IgnoreTemplateParameterMissing#### 11. Autres
##### 1. Vérification de la clé de paramètre du modèle
Depuis la version V1.24.0, par défaut la clé de paramètre manquante du modèle est ignorée et remplacée par une chaîne vide,
permet de contrôler le lancement ou non d’une exception.

Attribut Nom de colonne/Index/Ignorer pour Excel
#### 1. Spécifier le nom de la colonne, l’index de colonne, ignorer la colonne
Exemple Excel

Code
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 personnalisé (ExcelFormatAttribute)
Depuis la version V0.21.0, prise en charge des classes contenant la méthode ToString(string content) pour le format
Classe
csharp public class Dto { public string Name { get; set; }
[ExcelFormat("MMMM dd, yyyy")] public DateTime InDate { get; set; } }
Code
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);
Résultat
La requête prend en charge la conversion de format personnalisée

#### 3. Définir la largeur de colonne (ExcelColumnWidthAttribute)
csharp
public class Dto
{
[ExcelColumnWidth(20)]
public int ID { get; set; }
[ExcelColumnWidth(15.50)]
public string Name { get; set; }
}
#### 4. Plusieurs noms de colonnes correspondant à la même propriété.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
Depuis la version 1.24.0, le système prend en charge 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. ExcelColumnAttributeDepuis la version V1.26.0, plusieurs attributs peuvent être simplifiés comme suit :
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. DynamicColumnAttributeDepuis la version V1.26.0, nous pouvons définir dynamiquement les attributs de 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
Depuis la version V1.31.4, nous pouvons définir dynamiquement les attributs de Sheet. Nous pouvons définir le nom de la feuille et son état (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);
Nous pouvons également utiliser le nouvel attribut 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; }
}
### Ajouter, Supprimer, Mettre à jour#### Ajouter
v1.28.0 prend en charge l'insertion de N lignes de données CSV après la dernière ligne
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 prend en charge l'insertion d'une nouvelle feuille Excel dans un classeur existant
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");
}
#### Supprimer (en attente)#### Mettre à jour (en attente)
Vérification automatique du type Excel
- MiniExcel vérifiera par défaut s'il s'agit d'un xlsx ou csv en se basant sur « l'extension du fichier », mais il peut y avoir des inexactitudes, veuillez le spécifier manuellement.
- Le flux ne permet pas de savoir de quel excel il provient, veuillez le spécifier manuellement.
csharp
stream.SaveAs(excelType:ExcelType.CSV);
//or
stream.SaveAs(excelType:ExcelType.XLSX);
//or
stream.Query(excelType:ExcelType.CSV);
//or
stream.Query(excelType:ExcelType.XLSX);
csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { Seperator=';' }; MiniExcel.SaveAs(path, values,configuration: config);stringCSV
#### Remarque
- Par défaut, la valeur retournée est de type
et ne sera pas convertie en nombre ou en date/heure, sauf si le type est défini par un typage fort générique.,#### Séparateur personnalisé
Par défaut, le séparateur est
, vous pouvez modifier la propriétéSeperatorpour la personnalisation
Depuis la version V1.30.1, prise en charge de la fonction de séparateur personnalisé (merci @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);#### Saut de ligne personnalisé\r\nLa valeur par défaut est
comme caractère de nouvelle ligne, vous pouvez modifier la propriétéNewLinepour la personnaliser
#### Codage personnalisé- Le codage par défaut est "Détecter le codage à partir des marques d'ordre d'octets" (detectEncodingFromByteOrderMarks : true)
- Si vous avez des exigences de codage personnalisées, veuillez modifier la propriété 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);
#### Lire une chaîne vide comme nullPar défaut, les valeurs vides sont associées à string.Empty. Vous pouvez modifier ce comportement
csharp
var config = new MiniExcelLibs.Csv.CsvConfiguration()
{
ReadEmptyStringAsNull = true
};
### DataReader#### 1. GetReader
Depuis la version 1.23.0, vous pouvez utiliser 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);
}
}
}
### Asynchrone- v0.17.0 prend en charge l'asynchrone (merci à 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 prend en chargecancellationToken。Autres
#### 1. Enum
Assurez-vous que les noms dans Excel et les propriétés sont identiques, le système effectuera le mapping automatiquement (insensible à la casse)
Depuis la V0.18.0, la Description d'Enum est prise en charge
public enum Type { [Description("General User")] V1, [Description("General Administrator")] V2, [Description("Super Administrator")] V3 }

Depuis la version 1.30.0, la prise en charge de la conversion de Description Excel en Enum est disponible, merci à @KaneLeung
#### 2. Convertir CSV en XLSX ou convertir XLSX en CSV
csharp
MiniExcel.ConvertXlsxToCsv(xlsxPath, csvPath);
MiniExcel.ConvertXlsxToCsv(xlsxStream, csvStream);
MiniExcel.ConvertCsvToXlsx(csvPath, xlsxPath);
MiniExcel.ConvertCsvToXlsx(csvStream, xlsxStream);
#### 3. CultureInfo personnalisée`csharp using (var excelStream = new FileStream(path: filePath, FileMode.Open, FileAccess.Read)) using (var csvStream = new MemoryStream()) { MiniExcel.ConvertXlsxToCsv(excelStream, csvStream); }
Depuis la version 1.22.0, vous pouvez personnaliser CultureInfo comme ci-dessous, par défaut le système utilise CultureInfo.InvariantCulture.
var config = new CsvConfiguration()
{
Culture = new CultureInfo("fr-FR"),
};
MiniExcel.SaveAs(path, value, configuration: config);// or
MiniExcel.Query(path, configuration: config);
#### 4. Taille de mémoire tampon personnalisée public abstract class Configuration : IConfiguration
{
public int BufferSize { get; set; } = 1024 * 512;
}
#### 5. ModeRapideLe système ne contrôlera pas la mémoire, mais vous pouvez obtenir une vitesse de sauvegarde plus rapide.
var config = new OpenXmlConfiguration() { FastMode = true };
MiniExcel.SaveAs(path, reader,configuration:config);
#### 6. Ajout d'image en lot (MiniExcel.AddPicture)Veuillez ajouter les images avant de générer les lignes de données en lot, sinon le système utilisera beaucoup de mémoire lors de l'appel à 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. Obtenir les dimensions des feuilles
var dim = MiniExcel.GetSheetDimensions(path);
Exemples :
#### 1. SQLite & Dapper Fichier de grande taille Insertion SQL pour éviter OOM
remarque : veuillez ne pas appeler les méthodes ToList/ToArray après Query, cela chargerait toutes les données en mémoire
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();
}
}
performance :

#### 2. Démo API ASP.NET Core 3.1 ou MVC 5 pour téléchargement/téléversement Excel Xlsx Essayez-le
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. Requête de paginationvoid 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. Exportation Excel de WebForm via 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. Gestion dynamique de l’i18n multilingue et de l’autorité des rôlesComme dans l’exemple, créez une méthode pour gérer l’i18n et la gestion des autorisations, et utilisez yield return pour retourner IEnumerable
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 : Le titre d'en-tête Excel n'est pas égal au nom de propriété de la classe, comment faire le mapping ?
R. Veuillez utiliser l'attribut ExcelColumnName

#### Q. Comment interroger ou exporter plusieurs feuilles ?
R. Méthode GetSheetNames avec le paramètre de feuille Query sheetName.
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. Comment interroger ou exporter des informations sur la visibilité des feuilles ?
R. Méthode 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. Est-ce que l'utilisation de Count charge toutes les données en mémoire ?Non, le test d'image contient 1 million de lignes * 10 colonnes de données, l'utilisation maximale de la mémoire est <60 Mo, et cela prend 13,65 secondes

#### Q. Comment Query utilise-t-il les index entiers ?
L'index par défaut de Query est la clé chaîne de caractères : A,B,C.... Si vous souhaitez passer à un index numérique, veuillez créer la méthode suivante pour convertir
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. Aucun titre, un fichier Excel vide est généré lorsque la valeur est vide lors de l'exportation vers ExcelParce que MiniExcel utilise une logique similaire à JSON.NET pour obtenir dynamiquement le type à partir des valeurs afin de simplifier les opérations de l'API, le type ne peut pas être connu sans données. Vous pouvez consulter issue #133 pour comprendre.

Les types forts et DataTable génèrent des en-têtes, mais les Dictionary restent des fichiers Excel vides
#### Q. Comment arrêter le foreach lorsqu'une ligne est vide ?
MiniExcel peut être utilisé avec LINQ TakeWhile pour arrêter l'itérateur foreach.

#### Q. Comment supprimer les lignes vides ?

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. Comment utiliser SaveAs(path,value) pour remplacer un fichier existant sans générer l'erreur "Le fichier ...xlsx existe déjà"
Veuillez utiliser la classe Stream pour personnaliser la logique de création de fichier, par exemple :
`C#
using (var stream = File.Create("Demo.xlsx"))
MiniExcel.SaveAs(stream,value);
ou, depuis la V1.25.0, SaveAs prend en charge le paramètre overwriteFile pour activer/désactiver l'écrasement des fichiers existants
csharp MiniExcel.SaveAs(path, value, overwriteFile: true); ``
Limitations et mises en garde
- Ne prend pas en charge xls et les fichiers chiffrés pour l'instant
- xlsm prend uniquement en charge Query
Référence
ExcelDataReader / ClosedXML / Dapper / ExcelNumberFormat
Remerciements
#### Jetbrains

Merci de fournir gratuitement une licence All product IDE pour ce projet (Licence)
Partage et dons de contributions
Lien https://github.com/orgs/mini-software/discussions/754Contributeurs
--- Tranlated By Open Ai Tx | Last indexed: 2025-10-09 ---










