هذا المشروع هو جزء من مؤسسة .NET ويعمل تحت مدونة السلوك الخاصة بهم.
English | 简体中文 | 繁體中文 | 日本語 | 한국어 | हिन्दी | ไทย | Français | Deutsch | Español | Italiano | Русский | Português | Nederlands | Polski | العربية | فارسی | Türkçe | Tiếng Việt | Bahasa Indonesia
مقدمة
MiniExcel هو أداة لمعالجة ملفات Excel بسيطة وفعالة لمنصة .NET، مصممة خصيصًا لتقليل استخدام الذاكرة.
في الوقت الحالي، تتطلب معظم الأطر الشائعة تحميل جميع البيانات من مستند Excel إلى الذاكرة لتسهيل العمليات، ولكن قد يؤدي ذلك إلى مشاكل استهلاك الذاكرة. نهج MiniExcel مختلف: تتم معالجة البيانات صفًا بصف بطريقة تدفقية، مما يقلل الاستهلاك الأصلي من مئات الميجابايتات المحتملة إلى عدة ميجابايتات فقط، مما يمنع بشكل فعال مشكلات نفاد الذاكرة (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;
الميزات
- يقلل من استهلاك الذاكرة، مما يمنع أخطاء نفاد الذاكرة (OOM) ويتجنب عمليات جمع القمامة الكاملة
- يتيح عمليات البيانات في الوقت الفعلي وعلى مستوى الصف لتحسين الأداء مع مجموعات البيانات الكبيرة
- يدعم LINQ بالتنفيذ المؤجل، مما يسمح بالتقسيم السريع والكفء للذاكرة والاستعلامات المعقدة
- خفيف الوزن، ولا يحتاج إلى Microsoft Office أو مكونات COM+، وحجم ملف DLL أقل من 500 كيلوبايت
- واجهة برمجة تطبيقات بسيطة وبديهية لقراءة/كتابة/تعبئة ملفات Excel
ابدأ الآن
التثبيت
يمكنك تثبيت الحزمة من NuGet
ملاحظات الإصدار
يرجى مراجعة ملاحظات الإصدار
المهام القادمة
يرجى مراجعة المهام القادمة
الأداء
يمكنك العثور على كود اختبارات الأداء في MiniExcel.Benchmarks.
الملف المستخدم لاختبار الأداء هو Test1,000,000x10.xlsx، وهو مستند بحجم 32 ميغابايت يحتوي على 1,000,000 صف * 10 أعمدة، تم ملء خلاياه بالنص "HelloWorld".
لتشغيل جميع اختبارات الأداء استخدم:
dotnet run -project .\benchmarks\MiniExcel.Benchmarks -c Release -f net9.0 -filter * --join
يمكنك العثور على نتائج اختبارات الأداء للإصدار الأخير هنا.استعلام/استيراد Excel
#### 1. تنفيذ استعلام وربط النتائج مع IEnumerable ذو نوع قوي [[جرّب الآن]](https://dotnetfiddle.net/w5WD1J)
يوصى باستخدام Stream.Query لما يتمتع به من كفاءة أفضل.
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. تنفيذ استعلام وتعيينه إلى قائمة من الكائنات الديناميكية بدون استخدام الرأس [[جرّب ذلك]](https://dotnetfiddle.net/w5WD1J)
- المفتاح الديناميكي هو
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. تنفيذ استعلام مع أول صف كعنوان [[جرّب ذلك]](https://dotnetfiddle.net/w5WD1J)ملاحظة: في حال تكرار اسم العمود، يتم استخدام الأخير من اليمين
ملف إكسل المدخل:
| العمود1 | العمود2 | |-----------|---------| | 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. دعم استعلام امتدادات LINQ مثل First/Take/Skip ...إلخاستعلام 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);
}
الأداء بين MiniExcel/ExcelDataReader/ClosedXML/EPPlus

#### 5. الاستعلام حسب اسم الورقة
MiniExcel.Query(path, sheetName: "SheetName");
//or
stream.Query(sheetName: "SheetName");
#### 6. استعلام عن جميع أسماء الأوراق والصفوفvar sheetNames = MiniExcel.GetSheetNames(path);
foreach (var sheetName in sheetNames)
{
var rows = MiniExcel.Query(path, sheetName: sheetName);
}
#### 7. الحصول على الأعمدةvar columns = MiniExcel.GetColumns(path); // e.g result : ["A","B"...]var cnt = columns.Count; // get column count
#### 8. استعلام ديناميكي تحويل الصف إلى 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. استعلام إكسل يرجع DataTableغير مستحسن، لأن DataTable سيقوم بتحميل جميع البيانات في الذاكرة ويفقد ميزة الاستهلاك المنخفض للذاكرة في MiniExcel.
``C#
var table = MiniExcel.QueryAsDataTable(path, useHeaderRow: true);
#### 10. تحديد الخلية لبدء قراءة البيانات
csharp MiniExcel.Query(path,useHeaderRow:true,startCell:"B3")
csharp var config = new OpenXmlConfiguration() { FillMergedCells = true }; var rows = MiniExcel.Query(path, configuration: config);عدم استخدام تعبئة الدمج
#### 11. تعبئة الخلايا المدمجة
ملاحظة: الكفاءة أبطأ مقارنة بـ
السبب: معيار OpenXml يضع mergeCells في أسفل الملف، مما يؤدي إلى الحاجة لتكرار المرور على sheetxml مرتين

يدعم الطول والعرض المتغيرين وملء متعدد الصفوف والأعمدة

#### 12. قراءة الملفات الكبيرة باستخدام التخزين المؤقت على القرص (Disk-Base Cache - SharedString)
إذا تجاوز حجم SharedStrings 5 ميجابايت، فإن MiniExcel بشكل افتراضي سيستخدم التخزين المؤقت المحلي على القرص، مثل 10x100000.xlsx (بيانات مليون صف)، عند تعطيل التخزين المؤقت للقرص يكون الحد الأقصى لاستخدام الذاكرة 195 ميجابايت، ولكن عند تفعيل التخزين المؤقت للقرص يحتاج فقط إلى 65 ميجابايت. ملاحظة، يتطلب هذا التحسين بعض التكاليف في الكفاءة، لذا في هذه الحالة سيزيد وقت القراءة من 7.4 ثانية إلى 27.2 ثانية، إذا لم تكن بحاجة إليه يمكنك تعطيل التخزين المؤقت للقرص باستخدام الكود التالي:
csharp
var config = new OpenXmlConfiguration { EnableSharedStringCache = false };
MiniExcel.Query(path,configuration: config)
csharp var config = new OpenXmlConfiguration { SharedStringCacheSize=50010241024 }; MiniExcel.Query(path, configuration: config);يمكنك استخدام
SharedStringCacheSizeلتغيير حجم ملف sharedString بحيث يتجاوز الحجم المحدد لتخزين البيانات مؤقتًا على القرص


إنشاء/تصدير إكسل
- يجب أن يكون نوعًا غير مجرد مع مُنشئ عام بدون معلمات.
- يدعم MiniExcel تنفيذ IEnumerable المؤجل، إذا كنت ترغب في استخدام أقل قدر من الذاكرة، يرجى عدم استدعاء طرق مثل ToList
مثال: استخدام أو عدم استخدام ToList واستهلاك الذاكرة

#### 1. مجهول النوع أو نوع قوي [[جربه]](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> #### 2.
IEnumerable> #### 2.
IEnumerable>
csharp MiniExcel.SaveAs(path, reader);إنشاء نتيجة الملف :
موصى به| العمود1 | العمود2 | |-----------|---------| | MiniExcel | 1 | | Github | 2 |
#### 3. IDataReader
، يمكنه تجنب تحميل جميع البيانات في الذاكرة

تصدير أوراق متعددة باستخدام DataReader (موصى به بواسطة 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. جدول البيانات
غير موصى به، سيقوم بتحميل جميع البيانات في الذاكرة
يستخدم DataTable التسمية التوضيحية لاسم العمود أولاً، ثم يستخدم اسم العمود
MiniExcel.SaveAs(path, table);
csharp using (var connection = GetConnection(connectionString)) { var rows = connection.Query( new CommandDefinition( @"select 'MiniExcel' as Column1,1 as Column2 union all select 'Github',2" , flags: CommandFlags.NoCache) ); // Note: QueryAsync will throw close connection exception MiniExcel.SaveAs(path, rows); }#### 5. استعلام Dapper
CommandDefinition + CommandFlags.NoCacheشكرًا لـ @shaofing #552، يرجى استخدام
الكود أدناه سيحمّل جميع البيانات في الذاكرة
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. الحفظ كـ MemoryStream [[جرّب ذلك]](https://dotnetfiddle.net/JOen0e)
csharp
using (var stream = new MemoryStream()) //support FileStream,MemoryStream ect.
{
stream.SaveAs(values);
}
على سبيل المثال: واجهة برمجة تطبيقات لتصدير إكسل
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. إنشاء أوراق عمل متعددة
csharp
// 1. Dictionary// 2. DataSet var sheets = new DataSet(); sheets.Add(UsersDataTable); sheets.Add(DepartmentDataTable); //.. MiniExcel.SaveAs(path, sheets);

#### 8. خيارات TableStyles
النمط الافتراضي

بدون ضبط النمط
csharp
var config = new OpenXmlConfiguration()
{
TableStyles = TableStyles.None
};
MiniExcel.SaveAs(path, value,configuration:config);
csharp MiniExcel.SaveAs(path, value, configuration: new OpenXmlConfiguration() { AutoFilter = false });OpenXmlConfiguration.AutoFilter
#### 9. التصفية التلقائية (AutoFilter)
منذ الإصدار v0.19.0 يمكن لـ
تفعيل أو تعطيل التصفية التلقائية، والقيمة الافتراضية هي
true، وطريقة إعداد التصفية التلقائية تكون كالتالي:
#### 10. إنشاء صورة
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. تصدير ملف مصفوفة البايتات
منذ الإصدار 1.22.0، عندما يكون نوع القيمة هو
يقوم النظام بحفظ مسار الملف في الخلية بشكل افتراضي، وعند الاستيراد يمكن تحويلها إلى
byte[]. وإذا لم ترغب في استخدام ذلك، يمكنك تعيين
OpenXmlConfiguration.EnableConvertByteArrayإلى
false، مما يمكن أن يحسن من كفاءة النظام.
byte[]
منذ الإصدار 1.22.0، عندما يكون نوع القيمة هو
يقوم النظام بحفظ مسار الملف في الخلية بشكل افتراضي، وعند الاستيراد يمكن تحويلها إلى
byte[]. وإذا لم ترغب في استخدام ذلك، يمكنك تعيين
OpenXmlConfiguration.EnableConvertByteArrayإلى
false، مما يمكن أن يحسن من كفاءة النظام.
xlsx
#### 12. دمج الخلايا المتشابهة عموديًا
هذه الوظيفة مدعومة فقط في تنسيق
وتقوم بدمج الخلايا عموديًا بين وسوم @merge و @endmerge. يمكنك استخدام @mergelimit لتحديد حدود دمج الخلايا عموديًا.
var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
MiniExcel.MergeSameCells(mergedFilePath, path);
csharp
var memoryStream = new MemoryStream();var path = @"../../../../../samples/xlsx/TestMergeWithTag.xlsx";
memoryStream.MergeSameCells(path);
محتوى الملف قبل وبعد الدمج:بدون حد للدمج:


مع حد للدمج:


#### 13. تخطي القيم الفارغة
خيار صريح جديد لكتابة خلايا فارغة للقيم 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
السلوك السابق:
csharp
/ ... /OpenXmlConfiguration configuration = new OpenXmlConfiguration() { EnableWriteNullValueCell = false // Default value is true. };
MiniExcel.SaveAs(@"C:\temp\Book1.xlsx", dt, configuration: configuration);
xml
يعمل مع القيم null و DBNull.#### 14. تجميد الأعمدة والصفوف
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);{{اسم المتغير}}
تعبئة البيانات في قالب إكسل
- التصريح مشابه لقالب Vue
، أو عرض مجموعة البيانات
{{اسم المجموعة.اسم الحقل}}
عرض المجموعة يدعم IEnumerable/DataTable/DapperRow #### 1. تعبئة أساسية
القالب:
النتيجة:
الكود:
// 2. By Dictionary
var value = new Dictionary
#### 2. تعبئة بيانات IEnumerable
ملاحظة1: استخدم أول IEnumerable من نفس العمود كأساس لتعبئة القائمة
القالب:
النتيجة:
الكود: 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. تعبئة البيانات المعقدة
ملاحظة: يدعم جداول متعددة واستخدام نفس المتغير
القالب:
النتيجة:
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. تعبئة أداء البيانات الضخمة
ملاحظة: استخدام IEnumerable مع التنفيذ المؤجل بدلاً من ToList يمكن أن يوفر أقصى قدر من استهلاك الذاكرة في MiniExcel
#### 5. نوع التعيين التلقائي لقيمة الخلية
القالب
النتيجة
الفئة
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; } }
رمز
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. مثال : قائمة مشاريع Githubالنموذج

النتيجة

الكود
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. تعبئة البيانات المجمعة
csharp
var value = new Dictionarycsharp @if(name == Jack) {{employees.name}} @elseif(name == Neo) Test {{employees.name}} @else {{employees.department}} @endif##### 1. مع وسم
@groupومع وسم
@header
قبل
بعد
##### 2. مع وسم @group وبدون وسم @header
قبل
بعد
##### 3. بدون وسم @group
قبل
بعد
#### 8. عبارات If/ElseIf/Else داخل الخلية
القواعد:
- يدعم DateTime, Double, Int مع العوامل ==, !=, >, >=, <, <=.
- يدعم String مع العوامل ==, !=.
- يجب أن تكون كل عبارة في سطر جديد.
- يجب إضافة مسافة واحدة قبل وبعد العوامل.
- لا يجب أن يكون هناك سطر جديد داخل العبارات.
- يجب أن تكون الخلية بنفس التنسيق الموضح أدناه.
قبل
بعد

#### 9. DataTable كمعامل
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. الصيغ
$##### 1. مثال قم بإضافة بادئة
للمعادلة الخاصة بك واستخدم
$enumrowstartو
$enumrowendلتحديد مراجع بداية ونهاية الصفوف القابلة للتعداد:
$
عند عرض القالب، سيتم إزالة البادئة
وسيتم استبدال
$enumrowstartو
$enumrowendبأرقام الصفوف البداية والنهاية للقابل للتعداد:
$=SUM(C{{$enumrowstart}}:C{{$enumrowend}})
##### 2. أمثلة أخرى للصيغ:
| | | |--------------|-------------------------------------------------------------------------------------------| | الجمع |
| | متوسط بديل |
$=SUM(C{{$enumrowstart}}:C{{$enumrowend}}) / COUNT(C{{$enumrowstart}}:C{{$enumrowend}})| | النطاق |
$=MAX(C{{$enumrowstart}}:C{{$enumrowend}}) - MIN(C{{$enumrowstart}}:C{{$enumrowend}})|
IgnoreTemplateParameterMissing#### 11. أخرى
##### 1. التحقق من مفتاح معلمة القالب
منذ الإصدار V1.24.0، يتم افتراضياً تجاهل مفتاح معلمة القالب المفقود واستبداله بسلسلة فارغة، ويمكن لـ
التحكم في رمي الاستثناء أم لا.

اسم عمود إكسل / الفهرس / تجاهل السمة
#### 1. تحديد اسم العمود، فهرس العمود، تجاهل العمود
مثال إكسل

الكود
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. التنسيق المخصص (ExcelFormatAttribute)
منذ الإصدار V0.21.0 يدعم الفئة التي تحتوي على طريقة ToString(string content) للتنسيق
الفئة
csharp public class Dto { public string Name { get; set; }
[ExcelFormat("MMMM dd, yyyy")] public DateTime InDate { get; set; } }
رمز
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);
النتيجة
الاستعلام يدعم تحويل التنسيق المخصص

#### 3. تعيين عرض العمود (ExcelColumnWidthAttribute)
csharp
public class Dto
{
[ExcelColumnWidth(20)]
public int ID { get; set; }
[ExcelColumnWidth(15.50)]
public string Name { get; set; }
}
#### 4. تعيين أسماء أعمدة متعددة لنفس الخاصية.
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
منذ الإصدار 1.24.0، يدعم النظام 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)منذ الإصدار V1.26.0، يمكن تبسيط عدة خصائص كما يلي:
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منذ الإصدار V1.26.0، يمكننا تعيين خصائص العمود بشكل ديناميكي
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. خاصية الورقة الديناميكية
منذ الإصدار V1.31.4 يمكننا تعيين خصائص الورقة بشكل ديناميكي. يمكننا تعيين اسم الورقة وحالتها (مدى ظهورها).
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);
يمكننا أيضًا استخدام السمة الجديدة 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; }
}
### إضافة، حذف، تحديث#### إضافة
الإصدار v1.28.0 يدعم إدراج بيانات CSV لعدد N من الصفوف بعد الصف الأخير
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 يدعم إدراج ورقة جديدة في مصنف إكسل موجود
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);#### حذف(انتظار)
امتداد الملف#### تحديث(انتظار)
التحقق التلقائي من نوع Excel
- سيتحقق MiniExcel مما إذا كان الملف xlsx أو csv بناءً على
بشكل افتراضي، ولكن قد يكون هناك عدم دقة، يرجى تحديده يدويًا.
لا يمكن معرفة نوع ملف Excel من الـ Stream، يرجى تحديده يدويًا.
csharp var config = new MiniExcelLibs.Csv.CsvConfiguration() { Seperator=';' }; MiniExcel.SaveAs(path, values,configuration: config);string
CSV
#### ملاحظة
- النوع الافتراضي للعودة هو
، ولن يتم تحويل القيمة إلى أرقام أو تاريخ/وقت، ما لم يتم تعريف النوع عبر التعميم القوي.
,#### فاصل مخصص
الإعداد الافتراضي هو
كفاصل، يمكنك تعديل خاصية
Seperatorللتخصيص
منذ الإصدار V1.30.1 تم دعم وظيفة لتخصيص الفاصل (شكرًا لـ @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);#### فاصل الأسطر المخصص
\r\nالإعداد الافتراضي هو
كرمز للسطر الجديد، يمكنك تعديل خاصية
NewLineللتخصيص
#### الترميز المخصص- الترميز الافتراضي هو "كشف الترميز من علامات ترتيب البايتات" (detectEncodingFromByteOrderMarks: true)
- إذا كانت لديك متطلبات ترميز مخصصة، يرجى تعديل خاصية 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);
#### قراءة السلسلة الفارغة كقيمة فارغة (null)بشكل افتراضي، يتم تعيين القيم الفارغة إلى string.Empty. يمكنك تعديل هذا السلوك
csharp
var config = new MiniExcelLibs.Csv.CsvConfiguration()
{
ReadEmptyStringAsNull = true
};
### DataReader#### 1. GetReader
منذ الإصدار 1.23.0، يمكنك استخدام 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);
}
}
}
غير متزامن
- الإصدار v0.17.0 يدعم التشغيل غير المتزامن (شكرًا 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 الخيار
cancellationToken.
أخرى
#### 1. التعداد (Enum)
تأكد من أن اسم العمود في إكسل واسم الخاصية متطابقان، سيقوم النظام بالربط التلقائي بينهما (غير حساس لحالة الأحرف)
منذ الإصدار V0.18.0 يدعم وصف التعداد (Enum Description)
public enum Type { [Description("General User")] V1, [Description("General Administrator")] V2, [Description("Super Administrator")] V3 }

منذ الإصدار 1.30.0 أصبح يدعم التحويل من وصف Excel إلى Enum، شكرًا لـ @KaneLeung
#### 2. تحويل CSV إلى XLSX أو تحويل XLSX إلى CSV
csharp
MiniExcel.ConvertXlsxToCsv(xlsxPath, csvPath);
MiniExcel.ConvertXlsxToCsv(xlsxStream, csvStream);
MiniExcel.ConvertCsvToXlsx(csvPath, xlsxPath);
MiniExcel.ConvertCsvToXlsx(csvStream, xlsxStream);
#### 3. معلومات الثقافة المخصصة`
csharp using (var excelStream = new FileStream(path: filePath, FileMode.Open, FileAccess.Read)) using (var csvStream = new MemoryStream()) { MiniExcel.ConvertXlsxToCsv(excelStream, csvStream); }
منذ الإصدار 1.22.0، يمكنك تخصيص CultureInfo كما هو موضح أدناه، الافتراضي للنظام هو CultureInfo.InvariantCulture.
var config = new CsvConfiguration()
{
Culture = new CultureInfo("fr-FR"),
};
MiniExcel.SaveAs(path, value, configuration: config);// or
MiniExcel.Query(path, configuration: config);
#### 4. حجم المخزن المؤقت المخصص public abstract class Configuration : IConfiguration
{
public int BufferSize { get; set; } = 1024 * 512;
}
#### 5. وضع السرعةلن يتحكم النظام في الذاكرة، لكن يمكنك الحصول على سرعة حفظ أعلى.
var config = new OpenXmlConfiguration() { FastMode = true };
MiniExcel.SaveAs(path, reader,configuration:config);
#### 6. إضافة صورة دفعة واحدة (MiniExcel.AddPicture)يرجى إضافة الصور قبل إنشاء بيانات الصفوف دفعة واحدة، وإلا سيستهلك النظام ذاكرة كبيرة عند استدعاء 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. الحصول على أبعاد الأوراق
var dim = MiniExcel.GetSheetDimensions(path);
أمثلة:
#### 1. إدراج SQL لملف كبير الحجم باستخدام SQLite & Dapper لتجنب نفاد الذاكرة (OOM)
ملاحظة: يرجى عدم استدعاء طرائق ToList/ToArray بعد الاستعلام Query، لأنها ستقوم بتحميل جميع البيانات في الذاكرة
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();
}
}
الأداء:

#### 2. عرض توضيحي لواجهة برمجة التطبيقات لتحميل/تنزيل ملفات Excel Xlsx باستخدام ASP.NET Core 3.1 أو MVC 5 جربها
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. استعلام التصفح (Paging Query)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);
}

#### 4. تصدير Excel من WebForm باستخدام 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. إدارة ديناميكية للدعم متعدد اللغات (i18n) وصلاحيات الأدوار
على غرار المثال، أنشئ طريقة للتعامل مع إدارة i18n والصلاحيات، واستخدم yield return لإرجاع 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; }
}

الأسئلة الشائعة
#### س: عنوان رأس Excel لا يساوي اسم خاصية الصنف، كيف يمكن عمل التعيين؟
ج. يرجى استخدام السمة ExcelColumnName
#### س: كيف يمكن الاستعلام أو التصدير لعدة أوراق؟
ج. استخدم طريقة GetSheetNames مع معلمة 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);
}

#### س. كيف يمكن الاستعلام أو تصدير معلومات حول ظهور الورقة؟
ج. طريقة 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
}
#### س. هل استخدام Count سيؤدي إلى تحميل جميع البيانات في الذاكرة؟لا، اختبار الصورة يحتوي على مليون صف * 10 أعمدة من البيانات، وأقصى استخدام للذاكرة هو أقل من 60 ميجابايت، ويستغرق 13.65 ثانية
#### س. كيف يستخدم الاستعلام الفهارس الرقمية؟
الفهرس الافتراضي في الاستعلام هو مفتاح نصي: A,B,C.... إذا كنت تريد تغييره إلى فهرس رقمي، يرجى إنشاء الطريقة التالية للتحويل
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;
}
}
#### س. لماذا يتم إنشاء ملف Excel فارغ بدون عنوان عندما تكون القيمة فارغة عند التصدير إلى Excel؟لأن MiniExcel يستخدم منطقاً مشابهاً لـ JSON.NET للحصول على النوع ديناميكياً من القيم لتبسيط عمليات الـ API، ولا يمكن معرفة النوع بدون بيانات. يمكنك مراجعة المشكلة رقم 133 لمزيد من الفهم.
الأنواع القوية وDataTable ستُولّد رؤوس أعمدة، لكن Dictionary ستظل تولد Excel فارغ
#### س. كيف يمكن إيقاف foreach عند الصف الفارغ؟
يمكن استخدام MiniExcel مع LINQ TakeWhile لإيقاف حلقة foreach التكرارية.
#### س. كيف يمكن حذف الصفوف الفارغة؟
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;
}
}
جدول البيانات :
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;
}
#### س. كيف يمكن استخدام SaveAs(path,value) لاستبدال الملف الموجود دون ظهور خطأ "الملف ...xlsx موجود بالفعل"
يرجى استخدام فئة Stream لتخصيص منطق إنشاء الملف، على سبيل المثال:
`C#
using (var stream = File.Create("Demo.xlsx"))
MiniExcel.SaveAs(stream,value);
أو، منذ الإصدار V1.25.0، تدعم SaveAs معامل overwriteFile لتمكين/تعطيل الكتابة فوق الملف الموجود
csharp MiniExcel.SaveAs(path, value, overwriteFile: true); ``
القيود والمحاذير
- لا يتم دعم ملفات xls والملفات المشفرة حالياً
- ملفات xlsm تدعم فقط الاستعلام
المرجع
ExcelDataReader / ClosedXML / Dapper / ExcelNumberFormat
الشكر
#### Jetbrains
شكرًا لتوفير رخصة مجانية لجميع منتجات IDE لهذا المشروع (الرخصة)
مشاركة المساهمة والتبرع
الرابط https://github.com/orgs/mini-software/discussions/754المساهمون
--- Tranlated By Open Ai Tx | Last indexed: 2025-07-17 ---