Web Analytics

MiniExcel

⭐ 3234 stars French by mini-software

NuGet Statut de construction étoile Étoiles GitHub version Demander à DeepWiki


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


Vos étoiles ou dons peuvent rendre MiniExcel meilleur


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

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

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

image

#### 2. Exécuter une requête et la mapper sur une liste d’objets dynamiques sans utiliser head [[Essayez-le]](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. 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 ...etc

Requê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 queryfirst

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

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

var 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 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. Requête Excel retournant un DataTable

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

image

#### 10. Spécifiez la cellule à partir de laquelle commencer la lecture des données

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

#### 11. Remplir les cellules fusionnées

Remarque : L'efficacité est moindre comparée à l'absence de remplissage fusionné

Raison : La norme OpenXml place mergeCells en bas du fichier, ce qui oblige à parcourir deux fois le sheetxml

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

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

image

#### 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)
Vous pouvez utiliser SharedStringCacheSize pour modifier la taille du fichier sharedString au-delà de la taille spécifiée pour la mise en cache sur disque.
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);
image

image

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 image

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

csharp var values = new List>() { new Dictionary{{ "Column1", "MiniExcel" }, { "Column2", 1 } }, new Dictionary{{ "Column1", "Github" }, { "Column2", 2 } } }; MiniExcel.SaveAs(path, values);
Créer le résultat du fichier :

| Colonne1 | Colonne2 | |------------|----------| | MiniExcel | 1 | | Github | 2 |

#### 3. IDataReader

  • Recommandé, cela permet d'éviter de charger toutes les données en mémoire
csharp MiniExcel.SaveAs(path, reader);
image

Exportation de plusieurs feuilles avec DataReader (recommandé par 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

  • Non recommandé, cela chargera toutes les données en mémoire
  • DataTable utilise d'abord Caption pour le nom de la colonne, puis utilise columname
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. Requête Dapper

Merci @shaofing #552, veuillez utiliser 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); }
Le code ci-dessous chargera toutes les données en mémoire

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 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 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. Créer plusieurs feuilles

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. Options TableStyles

Style par défaut

image

Sans configuration de style

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

#### 9. Filtre automatique

Depuis la version v0.19.0, OpenXmlConfiguration.AutoFilter peut activer/désactiver le filtre automatique, la valeur par défaut est true, et la façon de configurer le filtre automatique est la suivante :

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

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

#### 11. Exportation de fichiers de tableaux d'octets

Depuis la version 1.22.0, lorsque le type de valeur est byte[], 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 en byte[]. Et si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez définir OpenXmlConfiguration.EnableConvertByteArray sur false, cela peut améliorer l'efficacité du système.

image

Depuis la version 1.22.0, lorsque le type de valeur est byte[], 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 en byte[]. Et si vous ne souhaitez pas utiliser cette fonctionnalité, vous pouvez définir OpenXmlConfiguration.EnableConvertByteArray sur false, cela peut améliorer l'efficacité du système.

image

#### 12. Fusionner les mêmes cellules verticalement

Cette fonctionnalité n'est supportée que dans le format xlsx 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.

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

Contenu du fichier avant et après fusion :

Sans limite de fusion :

Screenshot 2023-08-07 at 11 59 24

Screenshot 2023-08-07 at 11 59 57

Avec limite de fusion :

Screenshot 2023-08-08 at 18 21 00

Screenshot 2023-08-08 at 18 21 40

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

image

xml Somebody once told me.
Comportement précédent :

csharp / ... /

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

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

image

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

image

Remplir des données dans un modèle Excel

  • La déclaration est similaire au template Vue {{nom de variable}}, 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 : image

Résultat : image

Code :

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. Remplissage de données IEnumerable

Note1 : Utilisez le premier IEnumerable de la même colonne comme base pour remplir la liste

Modèle : image

Résultat : image

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() { ["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. Remplissage de Données Complexes

Remarque : Prend en charge les multi-feuilles et l’utilisation de la même variable

Modèle :

image

Résultat :

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

image

#### 5. Mappage automatique du type de valeur de cellule

Modèle

image

Résultat

image

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 Github

Modèle

image

Résultat

image

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ées

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. Avec la balise @group et avec la balise @header

Avant

before_with_header

Après

after_with_header

##### 2. Avec la balise @group et sans la balise @header

Avant

before_without_header

Après

after_without_header

##### 3. Sans la balise @group

Avant

without_group

Après

without_group_after

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

if_before

Après

if_after

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

##### 1. Exemple Préfixez votre formule avec $ et utilisez $enumrowstart et $enumrowend pour marquer les références au début et à la fin des lignes énumérables :

image

Lorsque le modèle est rendu, le préfixe $ sera supprimé et $enumrowstart et $enumrowend seront remplacés par les numéros de ligne de début et de fin de l’énumérable :

image

##### 2. Autres exemples de formules :

| | | |--------------|-------------------------------------------------------------------------------------------| | Somme | $=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) | | Moyenne alt. | $=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}}) | | Plage | $=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}}) |

#### 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, IgnoreTemplateParameterMissing permet de contrôler le lancement ou non d’une exception.

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

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

image

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

image

La requête prend en charge la conversion de format personnalisée

image

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

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

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

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

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

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

CSV

#### Remarque

  • Par défaut, la valeur retournée est de type string 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é Seperator pour la personnalisation

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

La valeur par défaut est \r\n comme caractère de nouvelle ligne, vous pouvez modifier la propriété NewLine pour la personnaliser

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

Par 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 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 prend en charge cancellationToken

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)

image

Depuis la V0.18.0, la Description d'Enum est prise en charge

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

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

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

Le 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);
Image

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

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

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. 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ôles

Comme 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> afin d’obtenir des effets de traitement dynamiques et à faible consommation de mémoire.

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

image

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

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

image

#### 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 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. Aucun titre, un fichier Excel vide est généré lorsque la valeur est vide lors de l'exportation vers Excel

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

image

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.

Image

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

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

jetbrains-variant-2

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/754

Contributeurs

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