You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core单例缓存IFormFile报错:无法访问已关闭流

问题根源

IFormFile 是 ASP.NET Core 请求上下文的绑定对象,请求处理完成后,框架会自动释放它关联的流资源。而你的 OrderService 是单例生命周期,其存活时长远超过单次请求,当后续从单例中取出订单并调用 OpenReadStream() 时,原 IFormFile 的流已经被销毁,因此抛出 ObjectDisposedException。

解决方案

核心原则:不要在单例或长生命周期对象中直接存储 IFormFile 实例,需要将文件内容持久化后再存储,常见两种实现方式:

方案1:将文件内容转为字节数组存储

适合小文件场景,直接把文件内容存在订单模型中:

  1. 修改订单相关模型,替换 IFormFile 为字节数组+元数据:
public class Watch
{
    public byte[] DialDesignFileContent { get; set; }
    public string DialDesignFileName { get; set; }
    public string DialDesignFileContentType { get; set; }
}
  1. 添加订单时,读取 IFormFile 内容到字节数组:
// 控制器接收订单请求示例
[HttpPost]
public IActionResult CreateOrder([FromForm] OrderCreateDto dto)
{
    var watch = new Watch();
    if (dto.DialDesignFile != null)
    {
        using var stream = new MemoryStream();
        dto.DialDesignFile.CopyTo(stream);
        watch.DialDesignFileContent = stream.ToArray();
        watch.DialDesignFileName = dto.DialDesignFile.FileName;
        watch.DialDesignFileContentType = dto.DialDesignFile.ContentType;
    }

    var order = new Order { Id = Guid.NewGuid().ToString(), Watch = watch };
    _orderService.AddOrder(order);
    return Ok();
}
  1. 生成邮件附件时,从字节数组创建流:
if (order.Watch.DialDesignFileContent != null)
{
    var attachment = new MimePart(order.Watch.DialDesignFileContentType)
    {
        Content = new MimeContent(new MemoryStream(order.Watch.DialDesignFileContent)),
        ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
        ContentTransferEncoding = ContentEncoding.Base64,
        FileName = order.Watch.DialDesignFileName
    };

    bodyBuilder.Attachments.Add(attachment);
}

方案2:将文件保存到本地/云存储,订单存储路径元数据

适合大文件场景,避免占用过多内存:

  1. 添加订单时,将文件保存到服务器本地或云存储:
// 控制器接收订单请求示例
[HttpPost]
public IActionResult CreateOrder([FromForm] OrderCreateDto dto)
{
    var watch = new Watch();
    if (dto.DialDesignFile != null)
    {
        // 定义文件存储路径(示例为wwwroot/uploads)
        var uploadDir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads");
        Directory.CreateDirectory(uploadDir);

        // 生成唯一文件名避免冲突
        var uniqueFileName = Guid.NewGuid().ToString() + Path.GetExtension(dto.DialDesignFile.FileName);
        var filePath = Path.Combine(uploadDir, uniqueFileName);

        using var stream = new FileStream(filePath, FileMode.Create);
        dto.DialDesignFile.CopyTo(stream);

        // 订单中存储文件路径和元数据
        watch.DialDesignFilePath = filePath;
        watch.DialDesignFileName = dto.DialDesignFile.FileName;
        watch.DialDesignFileContentType = dto.DialDesignFile.ContentType;
    }

    var order = new Order { Id = Guid.NewGuid().ToString(), Watch = watch };
    _orderService.AddOrder(order);
    return Ok();
}
  1. 生成邮件附件时,从存储路径读取文件:
if (!string.IsNullOrEmpty(order.Watch.DialDesignFilePath) && File.Exists(order.Watch.DialDesignFilePath))
{
    var attachment = new MimePart(order.Watch.DialDesignFileContentType)
    {
        Content = new MimeContent(new FileStream(order.Watch.DialDesignFilePath, FileMode.Open, FileAccess.Read)),
        ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
        ContentTransferEncoding = ContentEncoding.Base64,
        FileName = order.Watch.DialDesignFileName
    };

    bodyBuilder.Attachments.Add(attachment);
}
注意事项
  • 字节数组方案仅适合小文件,大文件建议用文件/云存储方案,避免内存溢出
  • 使用文件存储时,需考虑订单删除时同步清理对应文件,避免磁盘空间浪费
  • 任何场景下都不要将 IFormFile 直接存入单例、Session 等长生命周期对象

内容的提问来源于stack exchange,提问作者Hickori

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:42:44