使用CsvHelper在API控制器返回CSV时遇流已释放异常求助
解决ASP.NET Core导出CSV时的System.ObjectDisposedException错误
问题背景
- 预期目标:在API控制器中将Item对象列表导出为CSV文件
- 实现思路:定义Item类并构造测试列表,使用CsvHelper库结合MemoryStream生成CSV,通过FileResult返回文件
- 遇到的错误:运行时抛出
System.ObjectDisposedException: Cannot access a closed Stream.,即使给StreamWriter设置了leaveOpen:true仍未解决
错误代码
using Microsoft.AspNetCore.Mvc; using System.Globalization; using CsvHelper; namespace WebAPIExample2.Controllers { [Route("api/[controller]")] [ApiController] public class ExportProducts : ControllerBase { public class Item { public string Name { get; set; } public string Description { get; set; } } [HttpGet] public IActionResult GetProd() { List<Item> item = new List<Item>() { new Item { Name = "John", Description = "Brown" }, new Item { Name = "John", Description = "Brown" } }; // 创建存储CSV数据的内存流 using (var memoryStream = new MemoryStream()) using (var writer = new StreamWriter(memoryStream, leaveOpen:true)) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(item); writer.Flush(); memoryStream.Position = 0; // 将流位置重置到起始处 // 返回CSV文件 return File(memoryStream, "text/csv", "exported_items.csv"); } } } }
解决方案
移除memoryStream的using包裹,修改后的代码如下:
using Microsoft.AspNetCore.Mvc; using System.Globalization; using CsvHelper; namespace WebAPIExample2.Controllers { [Route("api/[controller]")] [ApiController] public class ExportProducts : ControllerBase { public class Item { public string Name { get; set; } public string Description { get; set; } } [HttpGet] public IActionResult GetProd() { var items = new List<Item>() { new Item { Name = "John", Description = "Brown" }, new Item { Name = "John", Description = "Brown" } }; var memoryStream = new MemoryStream(); using (var writer = new StreamWriter(memoryStream, leaveOpen: true)) using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture)) { csv.WriteRecords(items); writer.Flush(); memoryStream.Position = 0; } return File(memoryStream, "text/csv", "exported_items.csv"); } } }
原因分析
- 原代码中
memoryStream被using块包裹,当执行到return语句后,using块会自动调用memoryStream.Dispose()关闭流。而ASP.NET Core的FileResult是异步处理响应的,当它尝试读取流内容时,流已经被关闭,因此抛出异常。 - 给
StreamWriter设置leaveOpen:true仅能保证StreamWriter在Dispose时不关闭底层的memoryStream,但外层memoryStream的using块依然会在结束时关闭它,这才是问题的核心。 - ASP.NET Core的
FileResult在完成响应输出后,会自动负责释放流资源,因此无需手动用using包裹memoryStream。
内容的提问来源于stack exchange,提问作者Green Bicycle
相关产品推荐
相关产品推荐

