ASP.NET Core调用blobClient.UploadAsync()遇流关闭错误求助
向API发送multipart/form-data请求,请求包含IFormFile属性,控制器代码如下:
[Route("api/[controller]")] public class HomeController : ControllerBase { private readonly IBlobUploadService _blobUpload; public HomeController(IValidator<SubmitFileRequest> validator, IBlobUploadService blobUpload) { _blobUpload = blobUpload; } [HttpPost("SubmitFile")] public async Task<IActionResult> SubmitFile([FromForm]SubmitFileRequest request) { _blobUpload.UploadFile(request.File); return Ok(); } }
对应的Blob上传服务代码:
public interface IBlobUploadService { void UploadFile(IFormFile file); } public class BlobUploadService : IBlobUploadService { private string blobConnectionString = "my connection string"; private string blobContainer = "my container"; public async void UploadFile(IFormFile file) { BlobContainerClient container = new BlobContainerClient(blobConnectionString, blobContainer); var blobClient = container.GetBlobClient(file.FileName); await using (Stream? stream = file.OpenReadStream()) { await blobClient.UploadAsync(stream); } } }
执行await blobClient.UploadAsync(stream);时持续抛出异常:
System.AggregateException: 'Retry failed after 6 tries. Retry settings can be adjusted in ClientOptions.Retry or by configuring a custom retry policy in ClientOptions.RetryPolicy. (Error while copying content to a stream.) (Error while copying content to a stream.) (Error while copying content to a stream.) (Error while copying content to a stream.) (Error while copying content to a stream.) (Error while copying content to a stream.)'
Inner Exception 1: RequestFailedException: Error while copying content to a stream.
Inner Exception 2: HttpRequestException: Error while copying content to a stream.
Inner Exception 3: ObjectDisposedException: Cannot access a closed Stream.
更新:仅异步版本上传会触发异常,不使用await调用时可正常运行。
核心问题有两个:
async void的误用:UploadFile方法标记为async void,这种写法仅适用于事件处理程序,会导致调用方无法跟踪异步操作的完成状态,且异常无法被正确捕获和传播。- 未等待异步操作完成:控制器的
SubmitFile方法中调用_blobUpload.UploadFile(request.File)时没有使用await,导致控制器方法提前返回,ASP.NET Core会立即释放请求上下文相关资源(包括IFormFile对应的流),此时异步上传操作还在进行,就会出现流已被释放的错误。
按以下步骤修改代码:
修改服务接口和实现,将
void改为Task
接口和方法都要改为异步任务返回类型:public interface IBlobUploadService { Task UploadFile(IFormFile file); } public class BlobUploadService : IBlobUploadService { private string blobConnectionString = "my connection string"; private string blobContainer = "my container"; // 替换async void为async Task public async Task UploadFile(IFormFile file) { BlobContainerClient container = new BlobContainerClient(blobConnectionString, blobContainer); var blobClient = container.GetBlobClient(file.FileName); await using (Stream? stream = file.OpenReadStream()) { await blobClient.UploadAsync(stream); } } }控制器中等待异步操作完成
在SubmitFile方法中添加await关键字,确保上传操作完成后再返回响应:[HttpPost("SubmitFile")] public async Task<IActionResult> SubmitFile([FromForm]SubmitFileRequest request) { await _blobUpload.UploadFile(request.File); return Ok(); }可选:重置流位置(适配重试机制)
如果Blob客户端的重试机制需要重新读取流,可在上传前重置流的位置到起始处,避免重试时流已到末尾:await using (Stream? stream = file.OpenReadStream()) { stream.Position = 0; // 重置流位置 await blobClient.UploadAsync(stream); }
内容的提问来源于stack exchange,提问作者JackFord

