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

调用OpenReadStream()时出现Cannot access closed stream错误排查

解决IFormFile调用OpenReadStream时的ObjectDisposedException错误

我正在实现一个功能:让网站用户通过Web表单提交单个或多个文件,将这些文件作为附件发送给指定用户。已创建接收List<IFormFile>类型附件的Email Service,包含EmailModel类、PrepareEmailAttachments方法及测试代码。但调用attachment.OpenReadStream()时抛出System.ObjectDisposedException: Cannot access a closed Stream错误,已确认attachment中存在文件,但无法打开流,期望附件能加载到MimeKit用于邮件发送。

相关代码

EmailModel类

public class EmailModel
{
    public EmailAddressModel FromUser { get; private set; }
    public List<EmailAddressModel> ToAddresses { get; private set; }
    public List<EmailAddressModel> CcAddresses { get; private set; }
    public List<EmailAddressModel> BccAddresses { get; private set; }
    public List<IFormFile> Attachments { get; private set; }
    public string Subject { get; private set; }
    public string Body { get; private set; }
    public bool isHTML { get; private set; }

    public EmailModel(EmailAddressModel fromUser, List<EmailAddressModel> toAddresses, List<EmailAddressModel> ccAddresses, List<EmailAddressModel> bccAddresses, List<IFormFile> attachments, string subject, string body, bool isHTML)
    {
        FromUser = fromUser;
        ToAddresses = toAddresses;
        CcAddresses = ccAddresses;
        BccAddresses = bccAddresses;
        Attachments = attachments;
        Subject = subject;
        Body = body;
        this.isHTML = isHTML;
    }
}

PrepareEmailAttachments方法

private void PrepareEmailAttachments()
{
    foreach (IFormFile attachment in Email.Attachments)
    {
        if (attachment.Length > 0)
        {
            var stream = attachment.OpenReadStream();
            using (MemoryStream mStream = new MemoryStream())
            {
                    stream.CopyTo(mStream);
                    var data = mStream.ToArray();
                    Builder.Attachments.Add(attachment.FileName, data);
            }
        }
    }
}

测试代码

List<IFormFile> Attachments = new List<IFormFile>();
using (var stream = File.OpenRead("TestFile\\Test Document.pdf"))
{
    var file = new FormFile(stream, 0, stream.Length, null, Path.GetFileName(stream.Name))
    {
        Headers = new HeaderDictionary(),
        ContentType = "application/pdf"
    };
    Attachments.Add(file);
}


EmailModel Email = new EmailModel(
    From,
    To,
    CC,
    BCC,
    Attachments,
    "Test Email",
    "This is a build test",
    true);

错误信息

Message: 
Test method BA.Common.Tests.SendMail.SendEmail threw exception: 
System.ObjectDisposedException: Cannot access a closed Stream.

Stack Trace: 
BufferedFileStreamStrategy.Seek(Int64 offset, SeekOrigin origin)
FileStream.set_Position(Int64 value)
ReferenceReadStream.ctor(Stream inner, Int64 offset, Int64 length)
FormFile.OpenReadStream()
EmailService.PrepareEmailAttachments() line 111
EmailService.SendMail() line 43
SendMail.SendEmail() line 58

问题原因

测试代码中的using (var stream = File.OpenRead(...))块是罪魁祸首:当using块执行完毕,底层的FileStream会被自动释放,但你创建的FormFile对象持有了这个流的引用。后续调用OpenReadStream()时,尝试访问已经被释放的流,就会抛出ObjectDisposedException。

解决方案

方案1:测试中使用MemoryStream存储文件内容

修改测试代码,提前将文件内容读取到MemoryStream中,再用这个流创建FormFile。MemoryStream不依赖外部文件句柄,即使原文件流被释放,依然能正常读取:

List<IFormFile> Attachments = new List<IFormFile>();
// 先读取文件内容到字节数组
byte[] fileContent = File.ReadAllBytes("TestFile\\Test Document.pdf");
// 用字节数组创建MemoryStream
var memoryStream = new MemoryStream(fileContent);
// 创建FormFile对象
var formFile = new FormFile(memoryStream, 0, fileContent.Length, null, "Test Document.pdf")
{
    Headers = new HeaderDictionary(),
    ContentType = "application/pdf"
};
Attachments.Add(formFile);

方案2:优化PrepareEmailAttachments方法(可选)

虽然核心问题在测试代码,但可以优化附件处理逻辑,直接从IFormFile的流读取,避免依赖OpenReadStream()(本质上两者是同一个流,但写法更直观):

private void PrepareEmailAttachments()
{
    foreach (IFormFile attachment in Email.Attachments)
    {
        if (attachment.Length > 0)
        {
            using (MemoryStream mStream = new MemoryStream())
            {
                // 直接复制IFormFile的流,无需调用OpenReadStream()
                attachment.CopyTo(mStream);
                var data = mStream.ToArray();
                Builder.Attachments.Add(attachment.FileName, data);
            }
        }
    }
}

生产环境说明

在实际Web请求中,ASP.NET Core会自动管理IFormFile的流生命周期,请求处理期间流始终保持打开状态,不会出现这个问题。该错误仅出现在手动创建FormFile的测试场景中,只要正确处理底层流的生命周期即可避免。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 16:40:17