我正在尝试生成一个 Excel 文件,然后将其传递给 http 响应:
[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");
};
}
该文件在我测试时生成一次,但随后我不断收到以下错误:
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)
一直试图通过向 using 块添加流来解决,但没有成功
它看起来就像
XLWorkbook
中存在一个小故障,其中SaveAs
保持提供的流,并且它看起来就像Dispose()
然后被Fody挂钩来处理它。
这提供了两个选项:
using
中的using (var workbook = new XLWorkbook())
- 所以你只有var workbook = new XLWorkbook();
- 这是......丑陋且令人不满意,因为我们不知道这还没有处理什么SaveAs
的东西将会被烧毁考虑:
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");
这会重用底层缓冲区(没有额外的副本),但在未释放的第二个
MemoryStream
实例中。