Reputation: 1
I'm trying to generate an excel file and then pass it to http response:
[HttpPost("downloadCodesFile")]
public async Task<IActionResult> DownloadExcelFile(GetSeriesExcelSheetRequest request)
{
var stream = new MemoryStream();
using (var workbook = new XLWorkbook())
{
var codes = await _dataContext.Codes.Where(c => c.IsActive && c.SeriesNumber == request.SeriesNumber)
.Select(entry => entry.CodeName).ToListAsync();
var worksheet = workbook.Worksheets.Add("Sheet1");
// Set cell values
int i = 1;
foreach (var code in codes)
{
worksheet.Cell($"A{i}").Value = code;
i++;
}
workbook.SaveAs(stream);
stream.Position = 0;
return new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
};
}
The file got generated once when I testes but then I keep getting the following error:
System.ObjectDisposedException: Cannot access a closed Stream.
at System.IO.MemoryStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.MemoryStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Http.StreamCopyOperationInternal.CopyToAsync(Stream source, Stream destination, Nullable`1 count, Int32 bufferSize, CancellationToken cancel)
at Microsoft.AspNetCore.Internal.FileResultHelper.WriteFileAsync(HttpContext context, Stream fileStream, RangeItemHeaderValue range, Int64 rangeLength)
at Microsoft.AspNetCore.Mvc.Infrastructure.FileResultExecutorBase.WriteFileAsync(HttpContext context, Stream fileStream, RangeItemHeaderValue range, Int64 rangeLength)
at Microsoft.AspNetCore.Mvc.Infrastructure.FileStreamResultExecutor.ExecuteAsync(ActionContext context, FileStreamResult result)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
Been trying to figure out by adding stream to using block, without success
Upvotes: 0
Views: 327
Reputation: 1063884
It looks like there's a glitch in XLWorkbook
whereby SaveAs
keeps hold of the supplied stream, and it looks like Dispose()
is then hooked by Fody to dispose that.
This presents two options:
using
in using (var workbook = new XLWorkbook())
- so you just have var workbook = new XLWorkbook();
- this is ... ugly and unsatisfying, because we don't know what else this is not disposingSaveAs
is going to be burnedConsider:
Stream stream;
using (var workbook = new XLWorkbook())
{
// ... write the content etc
using var tmp = new MemoryStream(); // will be burned with workbook
workbook.SaveAs(tmp);
stream = new MemoryStream(tmp.GetBuffer(), 0, (int)tmp.Length);
};
return new FileStreamResult(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
This reuses the underlying buffer (no additional copies), but in a second MemoryStream
instance that is not disposed.
Upvotes: 2