ASP.NET Core FileStreamResult返回大Blob时偶现buffer空引用异常
解决ASP.NET Core返回大体积Azure Blob时FileStreamResult偶尔失败的问题
在Windows平台的.NET 6环境中,使用ASP.NET Core返回约1GB大小的Azure Blob时,FileStreamResult偶尔会执行失败。
相关代码示例
Azure.Storage.Blobs.BlobClient blob = container.GetBlobClient(fileName); Stream stream = await blob.OpenReadAsync().ConfigureAwait(false); return File(stream, MediaTypeNames.Application.Octet, fileName);
捕获到的FileStreamResultExecutor内部错误日志
System.ArgumentNullException: 值不能为空。(参数 'buffer') 在 System.Net.Http.HttpBaseStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) 在 Azure.Core.Pipeline.ReadTimeoutStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) 在 Azure.Core.Pipeline.RetriableStream.RetriableStreamImpl.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) 在 Azure.Core.Pipeline.RetriableStream.RetriableStreamImpl.RetryAsync(Exception exception, Boolean async, CancellationToken cancellationToken) 在 Azure.Core.Pipeline.RetriableStream.RetriableStreamImpl.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) 在 Azure.Storage.LazyLoadingReadOnlyStream`1.DownloadInternal(Boolean async, CancellationToken cancellationToken) 在 Azure.Storage.LazyLoadingReadOnlyStream`1.ReadInternal(Byte[] buffer, Int32 offset, Int32 count, Boolean async, CancellationToken cancellationToken) 在 Azure.Storage.LazyLoadingReadOnlyStream`1.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken) 在 Microsoft.AspNetCore.Http.StreamCopyOperationInternal.CopyToAsync(Stream source, Stream destination, Nullable`1 count, Int32 bufferSize, CancellationToken cancel) 在 Microsoft.AspNetCore.Internal.FileResultHelper.WriteFileAsync(HttpContext context, Stream fileStream, RangeItemHeaderValue range, Int64 rangeLength)
可行解决方案
1. 禁用Blob流的延迟加载
OpenReadAsync默认返回的延迟加载流(LazyLoadingReadOnlyStream)在处理大文件时,可能因异步加载时序问题导致buffer为空。通过配置BlobOpenReadOptions禁用延迟加载并调整缓冲区大小:
var options = new BlobOpenReadOptions(false) // 关闭延迟加载 { BufferSize = 4 * 1024 * 1024, // 设置4MB缓冲区,适配大文件传输 CancellationToken = HttpContext.RequestAborted }; Stream stream = await blob.OpenReadAsync(options).ConfigureAwait(false); return File(stream, MediaTypeNames.Application.Octet, fileName);
2. 传递请求取消令牌
确保Blob读取操作绑定请求的取消令牌,避免请求中断时流状态异常:
var cancellationToken = HttpContext.RequestAborted; Stream stream = await blob.OpenReadAsync(new BlobOpenReadOptions(false) { CancellationToken = cancellationToken }).ConfigureAwait(false); return File(stream, MediaTypeNames.Application.Octet, fileName);
3. 临时文件中转(适合超大文件)
将Blob下载到本地临时文件,再通过PhysicalFileResult返回,规避流操作的不稳定问题:
var tempFilePath = Path.GetTempFileName(); try { await blob.DownloadToAsync(tempFilePath, HttpContext.RequestAborted).ConfigureAwait(false); return PhysicalFile(tempFilePath, MediaTypeNames.Application.Octet, fileName); } finally { // 请求完成后异步清理临时文件 _ = Task.Run(() => File.Delete(tempFilePath), HttpContext.RequestAborted); }
内容的提问来源于stack exchange,提问作者Linas
相关产品推荐
相关产品推荐

