Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -401,5 +401,6 @@ FodyWeavers.xsd
/BenchmarkDotNet.Artifacts
/tests/MiniExcel.Tests.AspNetMvc/packages
/TestTemplate
/tests/data
/tests/MiniExcelTests/TemplateOptimization
/samples/xlsx/Test_EnableWriteFilePath.xlsx
120 changes: 69 additions & 51 deletions src/MiniExcel/Csv/CsvWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,74 +109,92 @@ private async Task<int> WriteValuesAsync(StreamWriter writer, object values, str
{
cancellationToken.ThrowIfCancellationRequested();

#if NETSTANDARD2_0_OR_GREATER || NET
IMiniExcelWriteAdapter writeAdapter = null;
if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out var asyncWriteAdapter))
#if NETSTANDARD2_0_OR_GREATER || NET
IAsyncMiniExcelWriteAdapter asyncWriteAdapter = null;
#endif
try
{
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
}
var props = writeAdapter != null ? writeAdapter.GetColumns() : await asyncWriteAdapter.GetColumnsAsync();
#if NETSTANDARD2_0_OR_GREATER || NET
if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out asyncWriteAdapter))
{
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
}

var props = writeAdapter != null
? writeAdapter.GetColumns()
: await asyncWriteAdapter.GetColumnsAsync();
#else
IMiniExcelWriteAdapter writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
var props = writeAdapter.GetColumns();
#endif
if (props == null)
{
await _writer.WriteAsync(_configuration.NewLine);
await _writer.FlushAsync();
return 0;
}

if (_printHeader)
{
await _writer.WriteAsync(GetHeader(props));
await _writer.WriteAsync(newLine);
}

var rowBuilder = new StringBuilder();
var rowsWritten = 0;

if (writeAdapter != null)
{
foreach (var row in writeAdapter.GetRows(props, cancellationToken))
if (props == null)
{
await _writer.WriteAsync(_configuration.NewLine);
await _writer.FlushAsync();
return 0;
}

if (_printHeader)
{
rowBuilder.Clear();
foreach (var column in row)
await _writer.WriteAsync(GetHeader(props));
await _writer.WriteAsync(newLine);
}

var rowBuilder = new StringBuilder();
var rowsWritten = 0;

if (writeAdapter != null)
{
foreach (var row in writeAdapter.GetRows(props, cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
AppendColumn(rowBuilder, column);
rowBuilder.Clear();
foreach (var column in row)
{
cancellationToken.ThrowIfCancellationRequested();
AppendColumn(rowBuilder, column);
}

RemoveTrailingSeparator(rowBuilder);
await _writer.WriteAsync(rowBuilder.ToString());
await _writer.WriteAsync(newLine);

rowsWritten++;
}

RemoveTrailingSeparator(rowBuilder);
await _writer.WriteAsync(rowBuilder.ToString());
await _writer.WriteAsync(newLine);

rowsWritten++;
}
}
#if NETSTANDARD2_0_OR_GREATER || NET
else
{
await foreach (var row in asyncWriteAdapter.GetRowsAsync(props, cancellationToken))
else
{
cancellationToken.ThrowIfCancellationRequested();
rowBuilder.Clear();

await foreach (var column in row)
await foreach (var row in asyncWriteAdapter.GetRowsAsync(props, cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
AppendColumn(rowBuilder, column);
rowBuilder.Clear();

await foreach (var column in row)
{
cancellationToken.ThrowIfCancellationRequested();
AppendColumn(rowBuilder, column);
}

RemoveTrailingSeparator(rowBuilder);
await _writer.WriteAsync(rowBuilder.ToString());
await _writer.WriteAsync(newLine);

rowsWritten++;
}

RemoveTrailingSeparator(rowBuilder);
await _writer.WriteAsync(rowBuilder.ToString());
await _writer.WriteAsync(newLine);

rowsWritten++;
}
#endif
return rowsWritten;
}
finally
{
#if NETSTANDARD2_0_OR_GREATER || NET
if (asyncWriteAdapter is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
#endif
return rowsWritten;
}
}

public async Task<int[]> SaveAsAsync(CancellationToken cancellationToken = default)
Expand Down
200 changes: 112 additions & 88 deletions src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,128 +171,152 @@ private static async Task WriteDimensionAsync(MiniExcelAsyncStreamWriter writer,
private async Task<int> WriteValuesAsync(MiniExcelAsyncStreamWriter writer, object values, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

#if NETSTANDARD2_0_OR_GREATER || NET

IMiniExcelWriteAdapter writeAdapter = null;
if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out var asyncWriteAdapter))
#if NETSTANDARD2_0_OR_GREATER || NET
IAsyncMiniExcelWriteAdapter asyncWriteAdapter = null;
#endif
try
{
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
}
#if NETSTANDARD2_0_OR_GREATER || NET
if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out asyncWriteAdapter))
{
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
}

var count = 0;
var isKnownCount = writeAdapter != null && writeAdapter.TryGetKnownCount(out count);
var props = writeAdapter != null ? writeAdapter?.GetColumns() : await asyncWriteAdapter.GetColumnsAsync();
var count = 0;
var isKnownCount = writeAdapter != null && writeAdapter.TryGetKnownCount(out count);
var props = writeAdapter != null
? writeAdapter?.GetColumns()
: await asyncWriteAdapter.GetColumnsAsync();
#else
IMiniExcelWriteAdapter writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);
writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration);

var isKnownCount = writeAdapter.TryGetKnownCount(out var count);
var props = writeAdapter.GetColumns();
var isKnownCount = writeAdapter.TryGetKnownCount(out var count);
var props = writeAdapter.GetColumns();
#endif

if (props == null)
{
await WriteEmptySheetAsync(writer);
return 0;
}
var maxColumnIndex = props.Count;
int maxRowIndex;
if (props == null)
{
await WriteEmptySheetAsync(writer);
return 0;
}

await writer.WriteAsync(WorksheetXml.StartWorksheetWithRelationship);
var maxColumnIndex = props.Count;
int maxRowIndex;

long dimensionPlaceholderPostition = 0;
await writer.WriteAsync(WorksheetXml.StartWorksheetWithRelationship);

// We can write the dimensions directly if the row count is known
if (_configuration.FastMode && !isKnownCount)
{
dimensionPlaceholderPostition = await WriteDimensionPlaceholderAsync(writer);
}
else if (isKnownCount)
{
maxRowIndex = count + (_printHeader ? 1 : 0);
await writer.WriteAsync(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, props.Count)));
}
long dimensionPlaceholderPostition = 0;

//sheet view
await writer.WriteAsync(GetSheetViews());
// We can write the dimensions directly if the row count is known
if (_configuration.FastMode && !isKnownCount)
{
dimensionPlaceholderPostition = await WriteDimensionPlaceholderAsync(writer);
}
else if (isKnownCount)
{
maxRowIndex = count + (_printHeader ? 1 : 0);
await writer.WriteAsync(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, props.Count)));
}

//cols:width
ExcelWidthCollection widths = null;
long columnWidthsPlaceholderPosition = 0;
if (_configuration.EnableAutoWidth)
{
columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, props);
widths = new ExcelWidthCollection(_configuration.MinWidth, _configuration.MaxWidth, props);
}
else
{
await WriteColumnsWidthsAsync(writer, ExcelColumnWidth.FromProps(props), cancellationToken);
}
//sheet view
await writer.WriteAsync(GetSheetViews());

//header
await writer.WriteAsync(WorksheetXml.StartSheetData);
var currentRowIndex = 0;
if (_printHeader)
{
await PrintHeaderAsync(writer, props, cancellationToken);
currentRowIndex++;
}
//cols:width
ExcelWidthCollection widths = null;
long columnWidthsPlaceholderPosition = 0;
if (_configuration.EnableAutoWidth)
{
columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, props);
widths = new ExcelWidthCollection(_configuration.MinWidth, _configuration.MaxWidth, props);
}
else
{
await WriteColumnsWidthsAsync(writer, ExcelColumnWidth.FromProps(props), cancellationToken);
}

if (writeAdapter != null)
{
foreach (var row in writeAdapter.GetRows(props, cancellationToken))
//header
await writer.WriteAsync(WorksheetXml.StartSheetData);
var currentRowIndex = 0;
if (_printHeader)
{
cancellationToken.ThrowIfCancellationRequested();

await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex));
foreach (var cellValue in row)
await PrintHeaderAsync(writer, props, cancellationToken);
currentRowIndex++;
}

if (writeAdapter != null)
{
foreach (var row in writeAdapter.GetRows(props, cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Prop, widths);

await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex));
foreach (var cellValue in row)
{
cancellationToken.ThrowIfCancellationRequested();
await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value,
cellValue.Prop, widths);
}

await writer.WriteAsync(WorksheetXml.EndRow);
}
await writer.WriteAsync(WorksheetXml.EndRow);
}
}
#if NETSTANDARD2_0_OR_GREATER || NET
else
{
await foreach (var row in asyncWriteAdapter.GetRowsAsync(props, cancellationToken))
else
{
cancellationToken.ThrowIfCancellationRequested();
await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex));

await foreach (var cellValue in row)
await foreach (var row in asyncWriteAdapter.GetRowsAsync(props, cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Prop, widths);
await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex));

await foreach (var cellValue in row)
{
cancellationToken.ThrowIfCancellationRequested();
await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value,
cellValue.Prop, widths);
}

await writer.WriteAsync(WorksheetXml.EndRow);
}
await writer.WriteAsync(WorksheetXml.EndRow);
}
}
#endif

maxRowIndex = currentRowIndex;
maxRowIndex = currentRowIndex;

await writer.WriteAsync(WorksheetXml.Drawing(_currentSheetIndex));
await writer.WriteAsync(WorksheetXml.EndSheetData);
await writer.WriteAsync(WorksheetXml.Drawing(_currentSheetIndex));
await writer.WriteAsync(WorksheetXml.EndSheetData);

if (_configuration.AutoFilter)
{
await writer.WriteAsync(WorksheetXml.Autofilter(GetDimensionRef(maxRowIndex, maxColumnIndex)));
}
if (_configuration.AutoFilter)
{
await writer.WriteAsync(WorksheetXml.Autofilter(GetDimensionRef(maxRowIndex, maxColumnIndex)));
}

await writer.WriteAsync(WorksheetXml.EndWorksheet);
await writer.WriteAsync(WorksheetXml.EndWorksheet);

if (_configuration.FastMode && dimensionPlaceholderPostition != 0)
{
await WriteDimensionAsync(writer, maxRowIndex, maxColumnIndex, dimensionPlaceholderPostition);
if (_configuration.FastMode && dimensionPlaceholderPostition != 0)
{
await WriteDimensionAsync(writer, maxRowIndex, maxColumnIndex, dimensionPlaceholderPostition);
}

if (_configuration.EnableAutoWidth)
{
await OverWriteColumnWidthPlaceholdersAsync(writer, columnWidthsPlaceholderPosition, widths.Columns,
cancellationToken);
}

var toSubtract = _printHeader ? 1 : 0;
return maxRowIndex - toSubtract;
}
if (_configuration.EnableAutoWidth)
finally
{
await OverWriteColumnWidthPlaceholdersAsync(writer, columnWidthsPlaceholderPosition, widths.Columns, cancellationToken);
#if NETSTANDARD2_0_OR_GREATER || NET
if (asyncWriteAdapter is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
#endif
}

var toSubtract = _printHeader ? 1 : 0;
return maxRowIndex - toSubtract;
}

private static async Task<long> WriteColumnWidthPlaceholdersAsync(MiniExcelAsyncStreamWriter writer, ICollection<ExcelColumnInfo> props)
Expand Down
Loading
Loading