ASP .NET 无法访问已处置的流

问题描述 投票:0回答:1

我正在尝试生成一个 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 块添加流来解决,但没有成功

c# closedxml
1个回答
0
投票

看起来就像

XLWorkbook
中存在一个小故障,其中
SaveAs
保持提供的流,并且它看起来就像
Dispose()
然后被Fody挂钩来处理它。

这提供了两个选项:

  1. 删除
    using
    中的
    using (var workbook = new XLWorkbook())
    - 所以你只有
    var workbook = new XLWorkbook();
    - 这是......丑陋且令人不满意,因为我们不知道这还没有处理什么
  2. 将数据复制出来,假设我们给
    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
实例中。

© www.soinside.com 2019 - 2024. All rights reserved.