From d4365111bcb7db8bda064382df30ac21606e72a1 Mon Sep 17 00:00:00 2001 From: WeiLin Date: Mon, 19 Sep 2022 21:17:47 +0800 Subject: [PATCH] [New] Support CSV Insert #I4X92G (via @shps951023) --- README.md | 40 +++++++++++ README.zh-CN.md | 38 ++++++++++ README.zh-Hant.md | 40 +++++++++++ docs/README.md | 4 ++ docs/README.zh-CN.md | 6 ++ docs/README.zh-Hant.md | 4 ++ src/MiniExcel/Csv/CsvWriter.cs | 10 ++- src/MiniExcel/IExcelWriter.cs | 1 + src/MiniExcel/MiniExcel.cs | 23 +++++++ src/MiniExcel/MiniExcelLibs.csproj | 16 ++--- .../OpenXml/ExcelOpenXmlSheetWriter.cs | 9 +++ tests/MiniExcelTests/MiniExcelIssueTests.cs | 69 ++++++++++++++++--- 12 files changed, 239 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ab9d8190..54cc74dc 100644 --- a/README.md +++ b/README.md @@ -934,6 +934,46 @@ Since V1.26.0, we can set the attributes of Column dynamically +### Add, Delete, Update + +#### Add + +v1.28.0 support CSV insert N rows data after last row + +```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](https://user-images.githubusercontent.com/12729184/191023733-1e2fa732-db5c-4a3a-9722-b891fe5aa069.png) + + + +#### Delete(waiting) + +#### Update(waiting) + + + ### Excel Type Auto Check - MiniExcel will check whether it is xlsx or csv based on the `file extension` by default, but there may be inaccuracy, please specify it manually. diff --git a/README.zh-CN.md b/README.zh-CN.md index e99c2b9a..ddee83fd 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -534,6 +534,8 @@ MiniExcel.SaveAs(path, value); + + ### 模板填充 Excel - 宣告方式类似 Vue 模板 `{{变量名称}}`, 或是集合渲染 `{{集合名称.栏位名称}}` @@ -933,7 +935,43 @@ public class TestIssueI4TXGTDto +### 新增、删除、修改 + +#### 新增 + +v1.28.0 开始支持 CSV 插入新增,在最后一行新增N笔数据 + +```csharp +// 原始数据 +{ + 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); +} +// 最后一行新增一行数据 +{ + var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) }; + MiniExcel.Insert(path, value); +} +// 最后一行新增N行数据 +{ + 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](https://user-images.githubusercontent.com/12729184/191023733-1e2fa732-db5c-4a3a-9722-b891fe5aa069.png) + + + +#### 删除(未完成) +#### 修改(未完成) ### Excel 类别自动判断 diff --git a/README.zh-Hant.md b/README.zh-Hant.md index 63e7f0cc..f15a116d 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -937,6 +937,46 @@ public class TestIssueI4TXGTDto +### 新增、刪除、修改 + +#### 新增 + +v1.28.0 開始支持 CSV 插入新增,在最後一行新增N筆數據 + +```csharp +// 原始數據 +{ + 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); +} +// 最後一行新增一行數據 +{ + var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) }; + MiniExcel.Insert(path, value); +} +// 最後一行新增N行數據 +{ + 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](https://user-images.githubusercontent.com/12729184/191023733-1e2fa732-db5c-4a3a-9722-b891fe5aa069.png) + + + +#### 刪除(未完成) + +#### 修改(未完成) + + + ### Excel 類別自動判斷 - MiniExcel 預設會根據`文件擴展名`判斷是 xlsx 還是 csv,但會有失準時候,請自行指定。 diff --git a/docs/README.md b/docs/README.md index c4f52f10..3ad65679 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,6 +24,10 @@ +## 1.28.0 + +- [New] Support CSV Insert #I4X92G (via @shps951023) + ### 1.27.0 - [New] Support DateTimeOffset and ExcelFormat #430 (via @Lightczx , @shps951023 ) diff --git a/docs/README.zh-CN.md b/docs/README.zh-CN.md index 44d817cc..e0aecc4b 100644 --- a/docs/README.zh-CN.md +++ b/docs/README.zh-CN.md @@ -27,6 +27,12 @@ + + +## 1.28.0 + +- [New] 支持 CSV Insert 方法 #I4X92G (via @shps951023) + ### 1.27.0 - [New] 支持 DateTimeOffset and ExcelFormat #430 (via @Lightczx , @shps951023 ) diff --git a/docs/README.zh-Hant.md b/docs/README.zh-Hant.md index e93d7efe..12aebca2 100644 --- a/docs/README.zh-Hant.md +++ b/docs/README.zh-Hant.md @@ -26,6 +26,10 @@ +## 1.28.0 + +- [New] 支持 CSV Insert 方法 #I4X92G (via @shps951023) + ### 1.27.0 - [New] 支持 DateTimeOffset and ExcelFormat #430 (via @Lightczx , @shps951023 ) diff --git a/src/MiniExcel/Csv/CsvWriter.cs b/src/MiniExcel/Csv/CsvWriter.cs index 7959e386..e3f237f7 100644 --- a/src/MiniExcel/Csv/CsvWriter.cs +++ b/src/MiniExcel/Csv/CsvWriter.cs @@ -18,9 +18,10 @@ internal class CsvWriter : IExcelWriter, IDisposable private readonly Stream _stream; private readonly CsvConfiguration _configuration; private readonly bool _printHeader; - private readonly object _value; + private object _value; private readonly StreamWriter _writer; private bool disposedValue; + private object _insertValue; public CsvWriter(Stream stream, object value, IConfiguration configuration, bool printHeader) { @@ -81,7 +82,7 @@ public void SaveAs() { mode = "Properties"; genericType = item.GetType(); - props = CustomPropertyHelper.GetSaveAsProperties(genericType,_configuration); + props = CustomPropertyHelper.GetSaveAsProperties(genericType, _configuration); } break; @@ -138,6 +139,11 @@ public void SaveAs() } } + public void Insert() + { + SaveAs(); + } + public async Task SaveAsAsync(CancellationToken cancellationToken = default(CancellationToken)) { await Task.Run(() => SaveAs(),cancellationToken).ConfigureAwait(false); diff --git a/src/MiniExcel/IExcelWriter.cs b/src/MiniExcel/IExcelWriter.cs index 2cdf1178..452dfe29 100644 --- a/src/MiniExcel/IExcelWriter.cs +++ b/src/MiniExcel/IExcelWriter.cs @@ -8,5 +8,6 @@ internal interface IExcelWriter { void SaveAs(); Task SaveAsAsync(CancellationToken cancellationToken = default(CancellationToken)); + void Insert(); } } diff --git a/src/MiniExcel/MiniExcel.cs b/src/MiniExcel/MiniExcel.cs index 207e2654..298fb228 100644 --- a/src/MiniExcel/MiniExcel.cs +++ b/src/MiniExcel/MiniExcel.cs @@ -4,6 +4,7 @@ using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; using System; + using System.Collections; using System.Collections.Generic; using System.Data; using System.Dynamic; @@ -24,6 +25,28 @@ public static MiniExcelDataReader GetReader(this Stream stream, bool useHeaderRo return new MiniExcelDataReader(stream, useHeaderRow, sheetName, excelType, startCell, configuration); } + public static void Insert(string path, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null) + { + if (Path.GetExtension(path).ToLowerInvariant() != ".csv") + throw new NotSupportedException("MiniExcel SaveAs only support csv insert now"); + + using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) + Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration); + } + + public static void Insert(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + { + // reuse code + object v = null; + { + if (!(value is IEnumerable) && !(value is IDataReader) && !(value is IDictionary) && !(value is IDictionary)) + v = Enumerable.Range(0, 1).Select(s => value); + else + v = value; + } + ExcelWriterFactory.GetProvider(stream, v, sheetName, excelType, configuration, false).Insert(); + } + public static void SaveAs(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null,bool overwriteFile = false) { if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") diff --git a/src/MiniExcel/MiniExcelLibs.csproj b/src/MiniExcel/MiniExcelLibs.csproj index 8afec0a4..71fdcc81 100644 --- a/src/MiniExcel/MiniExcelLibs.csproj +++ b/src/MiniExcel/MiniExcelLibs.csproj @@ -1,7 +1,7 @@  net45;netstandard2.0;net5.0 - 1.27.0 + 1.28.0 MiniExcel @@ -11,22 +11,22 @@ Fast, Low-Memory, Easy Excel .NET helper to import/export/template spreadsheet - Github : https://github.com/MiniExcel/MiniExcel + Github : https://github.com/mini-software/MiniExcel Gitee : https://gitee.com/dotnetchina/MiniExcel - Issues : https://github.com/MiniExcel/MiniExcel/issues - Todo : https://github.com/MiniExcel/MiniExcel/projects/1?fullscreen=true + Issues : https://github.com/mini-software/MiniExcel/issues + Todo : https://github.com/mini-software/MiniExcel/projects/1?fullscreen=true LIN,WEI-HAN MiniExcel LIN,WEI-HAN, 2021 onwards en - https://raw.githubusercontent.com/MiniExcel/MiniExcel/master/LICENSE + https://raw.githubusercontent.com/mini-software/MiniExcel/master/LICENSE MiniExcelLibs Apache-2.0 - https://github.com/MiniExcel/MiniExcel - https://github.com/MiniExcel/MiniExcel + https://github.com/mini-software/MiniExcel + https://github.com/mini-software/MiniExcel icon.png - Please Check [Release Notes](https://github.com/MiniExcel/MiniExcel/tree/master/docs) + Please Check [Release Notes](https://github.com/mini-software/MiniExcel/tree/master/docs) Github diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs index ee6fdc9e..598b277b 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs @@ -63,6 +63,10 @@ public ExcelOpenXmlSheetWriter(Stream stream, object value, string sheetName, IC _sheets.Add(new SheetDto { Name = sheetName, SheetIdx = 1 }); //TODO:remove } + public ExcelOpenXmlSheetWriter() + { + } + public void SaveAs() { GenerateDefaultOpenXml(); @@ -777,5 +781,10 @@ private string GetDimensionRef(int maxRowIndex, int maxColumnIndex) { await Task.Run(() => SaveAs(),cancellationToken).ConfigureAwait(false); } + + public void Insert() + { + throw new NotImplementedException(); + } } } diff --git a/tests/MiniExcelTests/MiniExcelIssueTests.cs b/tests/MiniExcelTests/MiniExcelIssueTests.cs index 80408ed7..e8b8ad1a 100644 --- a/tests/MiniExcelTests/MiniExcelIssueTests.cs +++ b/tests/MiniExcelTests/MiniExcelIssueTests.cs @@ -34,6 +34,52 @@ public MiniExcelIssueTests(ITestOutputHelper output) this.output = output; } + /// + /// https://gitee.com/dotnetchina/MiniExcel/issues/I4X92G + /// + [Fact] + public void TestIssueI4X92G() + { + var path = PathHelper.GetTempFilePath("csv"); + { + 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); + var content = File.ReadAllText(path); + Assert.Contains(@"ID,Name,InDate +1,Jack,""2021-01-03 00:00:00"" +2,Henry,""2020-05-03 00:00:00"" +", content); + } + { + var value = new { ID=3,Name = "Mike", InDate = new DateTime(2021, 04, 23) }; + MiniExcel.Insert(path, value); + var content = File.ReadAllText(path); + Assert.Equal(@"ID,Name,InDate +1,Jack,""2021-01-03 00:00:00"" +2,Henry,""2020-05-03 00:00:00"" +3,Mike,""2021-04-23 00:00:00"" +", content); + } + { + 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); + var content = File.ReadAllText(path); + Assert.Equal(@"ID,Name,InDate +1,Jack,""2021-01-03 00:00:00"" +2,Henry,""2020-05-03 00:00:00"" +3,Mike,""2021-04-23 00:00:00"" +4,Frank,""2021-06-07 00:00:00"" +5,Gloria,""2022-05-03 00:00:00"" +", content); + } + } + /// /// Exception : MiniExcelLibs.Exceptions.ExcelInvalidCastException: 'ColumnName : Date, CellRow : 2, Value : 2021-01-31 10:03:00 +08:00, it can't cast to DateTimeOffset type.' @@ -84,14 +130,15 @@ public void TestIssue_DataReaderSupportDimension() public void TestIssue413() { var path = PathHelper.GetTempFilePath(); - var value = new { - list = new List>{ + var value = new + { + list = new List>{ new Dictionary{ { "id","001"},{ "time",new DateTime(2022,12,25)} }, new Dictionary{ { "id","002"},{ "time",new DateTime(2022,9,23)} }, - } + } }; var templatePath = PathHelper.GetFile("xlsx/TestIssue413.xlsx"); - MiniExcel.SaveAsByTemplate(path,templatePath, value); + MiniExcel.SaveAsByTemplate(path, templatePath, value); var rows = MiniExcel.Query(path).ToList(); Assert.Equal("2022-12-25 00:00:00", rows[1].B); Assert.Equal("2022-09-23 00:00:00", rows[2].B); @@ -149,7 +196,7 @@ public void TestIssue370() }; var path = PathHelper.GetTempPath(); var json = JsonConvert.SerializeObject(new[] { new { id = 1, name = "Jack", createdate = new DateTime(2022, 04, 12), point = 123.456 } }, Formatting.Indented); - var value = JsonConvert.DeserializeObject>>(json); + var value = JsonConvert.DeserializeObject>>(json); MiniExcel.SaveAs(path, value, configuration: config); var rows = MiniExcel.Query(path, false).ToList(); @@ -166,7 +213,7 @@ public void TestIssue369() { var config = new OpenXmlConfiguration { - DynamicColumns = new DynamicExcelColumn[] { + 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}, @@ -174,7 +221,7 @@ public void TestIssue369() } }; var path = PathHelper.GetTempPath(); - var value = new[] { new { id = 1, name = "Jack", createdate = new DateTime(2022, 04, 12) ,point = 123.456} }; + var value = new[] { new { id = 1, name = "Jack", createdate = new DateTime(2022, 04, 12), point = 123.456 } }; MiniExcel.SaveAs(path, value, configuration: config); var rows = MiniExcel.Query(path, false).ToList(); @@ -204,9 +251,9 @@ public void TestIssueI4ZYUU() public class TestIssueI4ZYUUDto { - [ExcelColumn(Name = "ID",Index =0)] + [ExcelColumn(Name = "ID", Index = 0)] public string MyProperty { get; set; } - [ExcelColumn(Name = "CreateDate", Index = 1,Format ="yyyy-MM",Width =100)] + [ExcelColumn(Name = "CreateDate", Index = 1, Format = "yyyy-MM", Width = 100)] public DateTime MyProperty2 { get; set; } } @@ -360,7 +407,7 @@ public void TestIssue401(bool autoFilter, int count) Assert.Equal(count, cnt); File.Delete(path); } - + { var xlsxPath = @"../../../../../samples/xlsx/Test5x2.xlsx"; var tempSqlitePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.db"); @@ -1666,7 +1713,7 @@ public class Issue255DTO { [ExcelFormat("yyyy")] public DateTime Time { get; set; } - + [ExcelColumn(Format = "yyyy")] public DateTime Time2 { get; set; } }