IFormFileCollection上传Azure Blob报无法访问已释放对象错误
问题原因
你触发的Cannot access a disposed object. Object name: 'FileBufferingReadStream'错误,本质是IFormFile关联的FileBufferingReadStream生命周期由ASP.NET Core请求管道托管,出现提前释放的常见诱因有三个:
- 直接将
IFormFile.OpenReadStream()返回的原始流传给Blob上传方法,没有对流做独立拷贝:当请求上下文切换、表单资源被框架提前回收时,流会被自动释放,上传逻辑读取流时就会触发对象已释放错误 - 旧版Azure Blob SDK(
Microsoft.Azure.Storage.Blob)的UploadFromStreamAsync方法不会自动重置流读取位置,如果流位置不在起始点,会触发读取失败,部分场景下会间接导致流被框架回收 - 错误的异常捕获写法
throw e会清空原始异常堆栈,导致你无法定位流释放的真实触发点,同时如果将IFormFileCollection传到脱离请求管道的后台线程、延迟任务中读取,会因为请求结束后流被释放100%触发该错误
修复步骤
1. 配置请求表单缓冲
在Program.cs中配置表单选项,保证文件上传场景下请求体缓冲正常开启,避免大文件场景下流被提前回收:
builder.Services.Configure<FormOptions>(options => { // 根据业务需求调整单文件/请求体大小上限,示例为100MB options.MultipartBodyLengthLimit = 100 * 1024 * 1024; options.BufferBody = true; });
2. 修正Blob上传逻辑
不要直接使用IFormFile返回的原始流上传,先将文件内容拷贝到自己控制生命周期的内存流中,上传前重置流位置:
public async Task UploadAsync(IFormFileCollection files, string directoryName) { var blobContainer = await _azureBlobConnectionFactory.GetBlobContainer(); CloudBlobDirectory directory = blobContainer.GetDirectoryReference(directoryName); for (int i = 0; i < files.Count; i++) { IFormFile file = files[i]; CloudBlockBlob blockblob = directory.GetBlockBlobReference(file.FileName); // 新建独立内存流拷贝文件内容,生命周期由当前代码块控制,不受请求管道回收影响 using (var uploadStream = new MemoryStream()) { await file.CopyToAsync(uploadStream); uploadStream.Position = 0; // 将流读取指针重置到起始位置 await blockblob.UploadFromStreamAsync(uploadStream); } } }
3. 修正业务层异常写法
删除无意义的异常包装,不要用throw e破坏异常堆栈,同时确保上传逻辑在当前请求生命周期内执行,不要把IFormFile对象传递到请求结束后才运行的延迟任务、后台队列中:
public async Task<bool> UploadToBlob(DocumentModel model,string directorypath) { await _blobService.UploadAsync(model.Files, "Documents/dummy.pdf"); return true; // 如果需要加异常日志,按如下写法,不要用throw e // catch(Exception e) // { // _logger.LogError(e, "Blob文件上传失败"); // throw; // } }
补充建议:如果项目还在使用旧版
Microsoft.Azure.Storage.Blob包,建议迁移到新版Azure.Storage.BlobsSDK,新版SDK对流处理的兼容性更好,上传方法会自动处理流位置,能减少很多流相关的异常。
内容的提问来源于stack exchange,提问作者Md Aslam
相关产品推荐
相关产品推荐

