diff --git a/src/MiniExcel/Csv/CsvWriter.cs b/src/MiniExcel/Csv/CsvWriter.cs index ee50da7d..b56fbef3 100644 --- a/src/MiniExcel/Csv/CsvWriter.cs +++ b/src/MiniExcel/Csv/CsvWriter.cs @@ -135,6 +135,11 @@ public void Insert() SaveAs(); } + public void InsertSheet(bool overwriteSheet) + { + throw new NotImplementedException(); + } + 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 b8fb6d05..86227df9 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(); + void InsertSheet(bool overwriteSheet); } } diff --git a/src/MiniExcel/MiniExcel.cs b/src/MiniExcel/MiniExcel.cs index bcfe6620..bb33b725 100644 --- a/src/MiniExcel/MiniExcel.cs +++ b/src/MiniExcel/MiniExcel.cs @@ -27,7 +27,7 @@ public static MiniExcelDataReader GetReader(this Stream stream, bool useHeaderRo 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"); + throw new NotSupportedException("MiniExcel 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); @@ -35,6 +35,9 @@ public static void Insert(string path, object value, string sheetName = "Sheet1" public static void Insert(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) { + if (excelType != ExcelType.CSV) + throw new NotSupportedException("MiniExcel only support csv insert now"); + // reuse code object v = null; { @@ -43,9 +46,38 @@ public static void Insert(this Stream stream, object value, string sheetName = " else v = value; } + stream.Seek(0, SeekOrigin.End); ExcelWriterFactory.GetProvider(stream, v, sheetName, excelType, configuration, false).Insert(); } + public static void InsertSheet(string path, object value, string sheetName, bool printHeader = true, ExcelType excelType = ExcelType.UNKNOWN, bool overwriteSheet = false) + { + if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") + throw new NotSupportedException("MiniExcel SaveAs not support xlsm"); + + if (!File.Exists(path)) + { + SaveAs(path, value, printHeader, sheetName, excelType); + } + else + { + using (var stream = new FileStream(path, FileMode.Open)) + InsertSheet(stream, value, sheetName, printHeader, ExcelTypeHelper.GetExcelType(path, excelType), overwriteSheet); + } + } + + public static void InsertSheet(this Stream stream, object value, string sheetName, bool printHeader = true, ExcelType excelType = ExcelType.XLSX, bool overwriteSheet = false) + { + var configuration = new OpenXmlConfiguration + { + FreezeRowCount = 0, + AutoFilter = false, + FastMode = true, + TableStyles = TableStyles.None + }; + ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).InsertSheet(overwriteSheet); + } + 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/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs index d43fd029..303a055f 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs @@ -45,6 +45,69 @@ public ExcelOpenXmlSheetWriter() { } + public void InsertSheet(bool overwriteSheet) + { + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Styles)?.Delete(); + GenerateStylesXml();//TODO: Style调整为非固定的,支持动态增加单元格样式,这样InsertSheet就不会覆盖掉其他Sheet的样式 + + var sheetRecords = new ExcelOpenXmlSheetReader(_stream, _configuration).GetWorkbookRels(_archive.Entries).ToArray(); + foreach (var sheetRecord in sheetRecords.OrderBy(o => o.Id)) + { + _sheets.Add(new SheetDto { Name = sheetRecord.Name, SheetIdx = (int)sheetRecord.Id, State = sheetRecord.State }); + } + var existSheetDto = _sheets.SingleOrDefault(s => s.Name == _defaultSheetName); + if (existSheetDto == null) + { + currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; + var insertSheetInfo = GetSheetInfos(_defaultSheetName); + var insertSheetDto = insertSheetInfo.ToDto(currentSheetIndex); + _sheets.Add(insertSheetDto); + CreateSheetXml(_value, insertSheetDto.Path); + } + else if (overwriteSheet) + { + _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); + currentSheetIndex = existSheetDto.SheetIdx; + CreateSheetXml(_value, existSheetDto.Path); + } + else + { + throw new Exception($"Sheet “{_defaultSheetName}” already exist"); + } + + AddFilesToZip(); + + GenerateDrawinRelXml(currentSheetIndex); + + GenerateDrawingXml(currentSheetIndex); + + GenerateWorkBookXmls(out StringBuilder workbookXml, out StringBuilder workbookRelsXml, out Dictionary sheetsRelsXml); + + foreach (var sheetRelsXml in sheetsRelsXml) + { + var sheetRelsXmlPath = ExcelFileNames.SheetRels(sheetRelsXml.Key); + _archive.Entries.SingleOrDefault(s => s.FullName == sheetRelsXmlPath)?.Delete(); + CreateZipEntry( + sheetRelsXmlPath, + null, + ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value)); + } + + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Workbook)?.Delete(); + CreateZipEntry( + ExcelFileNames.Workbook, + ExcelContentTypes.Workbook, + ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString())); + + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.WorkbookRels)?.Delete(); + CreateZipEntry( + ExcelFileNames.WorkbookRels, + null, + ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString())); + + _archive.Dispose(); + } + public void SaveAs() { GenerateDefaultOpenXml(); @@ -337,7 +400,7 @@ private void GenerateSheetByDataTable(MiniExcelStreamWriter writer, DataTable va var prop = GetColumnInfosFromDynamicConfiguration(columnName); props.Add(prop); } - + //sheet view WriteSheetViews(writer); @@ -398,7 +461,8 @@ private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable writer.Write(WorksheetXml.EndCols); } - private void WriteSheetViews(MiniExcelStreamWriter writer) { + private void WriteSheetViews(MiniExcelStreamWriter writer) + { // exit early if no style to write if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0) { @@ -417,7 +481,8 @@ private void WriteSheetViews(MiniExcelStreamWriter writer) { writer.Write(WorksheetXml.EndSheetViews); } - private void WritePanes(MiniExcelStreamWriter writer) { + private void WritePanes(MiniExcelStreamWriter writer) + { string activePane; if (_configuration.FreezeColumnCount > 0 && _configuration.FreezeRowCount > 0) @@ -432,7 +497,7 @@ private void WritePanes(MiniExcelStreamWriter writer) { { activePane = "bottomLeft"; } - writer.Write( WorksheetXml.StartPane( + writer.Write(WorksheetXml.StartPane( xSplit: _configuration.FreezeColumnCount > 0 ? _configuration.FreezeColumnCount : (int?)null, ySplit: _configuration.FreezeRowCount > 0 ? _configuration.FreezeRowCount : (int?)null, topLeftCell: ExcelOpenXmlUtils.ConvertXyToCell( @@ -441,7 +506,7 @@ private void WritePanes(MiniExcelStreamWriter writer) { ), activePane: activePane, state: "frozen" - ) ); + )); // write pane selections if (_configuration.FreezeColumnCount > 0 && _configuration.FreezeRowCount > 0) @@ -452,16 +517,16 @@ private void WritePanes(MiniExcelStreamWriter writer) { */ - var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount+1, 1); + var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount + 1, 1); writer.Write(WorksheetXml.PaneSelection("topRight", cellTR, cellTR)); - var cellBL = ExcelOpenXmlUtils.ConvertXyToCell(1, _configuration.FreezeRowCount+1); + var cellBL = ExcelOpenXmlUtils.ConvertXyToCell(1, _configuration.FreezeRowCount + 1); writer.Write(WorksheetXml.PaneSelection("bottomLeft", cellBL, cellBL)); - var cellBR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount+1, _configuration.FreezeRowCount+1); + var cellBR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount + 1, _configuration.FreezeRowCount + 1); writer.Write(WorksheetXml.PaneSelection("bottomRight", cellBR, cellBR)); } - else if ( _configuration.FreezeColumnCount > 0 ) + else if (_configuration.FreezeColumnCount > 0) { // freeze column /* @@ -610,27 +675,37 @@ private void GenerateDrawinRelXml() { for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - var drawing = GetDrawingRelationshipXml(sheetIndex); - CreateZipEntry( - ExcelFileNames.DrawingRels(sheetIndex), - null, - ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing)); + GenerateDrawinRelXml(sheetIndex); } } + private void GenerateDrawinRelXml(int sheetIndex) + { + var drawing = GetDrawingRelationshipXml(sheetIndex); + CreateZipEntry( + ExcelFileNames.DrawingRels(sheetIndex), + null, + ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing)); + } + private void GenerateDrawingXml() { for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - var drawing = GetDrawingXml(sheetIndex); - - CreateZipEntry( - ExcelFileNames.Drawing(sheetIndex), - ExcelContentTypes.Drawing, - ExcelXml.DefaultDrawing.Replace("{{format}}", drawing)); + GenerateDrawingXml(sheetIndex); } } + private void GenerateDrawingXml(int sheetIndex) + { + var drawing = GetDrawingXml(sheetIndex); + + CreateZipEntry( + ExcelFileNames.Drawing(sheetIndex), + ExcelContentTypes.Drawing, + ExcelXml.DefaultDrawing.Replace("{{format}}", drawing)); + } + /// /// workbook.xml、workbookRelsXml ///