【问题标题】:Stream closed error when adding attachment to MailMessage将附件添加到 MailMessage 时出现流关闭错误
【发布时间】:2011-03-09 21:17:07
【问题描述】:

我使用以下代码将文件附加到电子邮件中。

msg = new MailMessage();

    using (strMem = new MemoryStream((byte[])attDr["filedata"]))
    {
        using (strWriter = new StreamWriter(strMem))
        {
            strWriter.Flush(); strMem.Position = 0;
            using (attachment = new Attachment(strMem, attDr["filename"].ToString()))
            {
                msg.Attachments.Add(attachment);
            }
        }
    }

...
...
msg.Send();  //Error: System.ObjectDisposedException: Cannot access a closed Stream.

错误信息是://Error: System.ObjectDisposedException: Cannot access a closed Stream

我猜“USING”语句在退出块时会关闭流。但是为什么“Attacments.Add()”不制作自己的流副本呢?

【问题讨论】:

  • 欢迎来到StreamWriter的魔法世界。它总是关闭包裹的Stream。 .NET 框架中奇妙的设计怪癖之一 :-)
  • 这只是因为在使用块结束时它会处理对象附件。但是附件是由味精使用的。所以....
  • 从未声明过msg 变量。 MailMessage 类也不包含任何 Send() 方法。

标签: c# .net email


【解决方案1】:

Send() 方法将访问附件以将它们嵌入到邮件消息中。到这里就大功告成了,内存流被处理掉了。您需要在 using 语句中移动 Send() 调用,以便在发送消息之后之前不会释放流。

在这里提到 using 不是必需的,因为内存流没有任何需要处置的非托管资源总是让我在 SO 遇到麻烦。所以我就不提了。

【讨论】:

  • 我喜欢你不提这个。 :)
【解决方案2】:

当我处理 MailMessage 与发送分开创建并且可能带有多个附件的场景时,我发现的一个解决方案是使用 MemoryStream 对象列表

// Tuple contains displayName and byte[] content for attachment in this case
List<Tuple<string,byte[]>> attachmentContent;
MailMessage email = BuildNativeEmail(requestObject, out attachmentContent);
List<MemoryStream> streams = new List<MemoryStream>();
foreach(Tuple<string,byte[]> attachment in attachmentContent)
{
    MemoryStream m = new MemoryStream(attachment.Item2);
    email.Attachments.Add(new System.Net.Mail.Attachment(m, attachment.Item1));
    streams.add(m);
}

// client represents a System.Net.Mail.SmtpClient object
client.Send(email);

foreach(MemoryStream stream in streams)
{
    stream.Dispose();
}

【讨论】:

    【解决方案3】:

    Yes.It's true.Using 语句在退出块时关闭流。

    不要使用“Using”语句。发送电子邮件后调用dispose()方法处理附件

    ...
    ...
    msg.Send();
    
    msg.Attachments.ToList().ForEach(x => x.ContentStream.Dispose());
    

    您也可以对 AlternateView 使用相同的方式。

    ...
    ...
    msg.AlternateViews.Add(alternateView);
    
    ...
    msg.Send();
    ...
    if (alternateView != null) {
        mail.AlternateViews.Dispose();
    }
    

    【讨论】:

    • 非常简单直接的解决方案。
    【解决方案4】:

    使用:

    using (strMem = new MemoryStream((byte[])attDr["filedata"]))
    {
        using (strWriter = new StreamWriter(strMem))
        {
            strWriter.Flush(); strMem.Position = 0;
            using (attachment = new Attachment(attDr["filename"].ToString()))
            {
                strMem.CopyTo(attachment.ContentStream);
                msg.Attachments.Add(attachment);
            }
        }
    }
    

    【讨论】:

    • 我从创建附件的行中删除了 USING,但仍然出现相同的错误。 strMem 也不支持“CopyTo”,所以我不得不使用 strMem.WriteTo(attachment.ContentStream);
    • 我不知道是什么触发了错误。我确实认为 USING 以某种方式参与其中! :)
    • 正在使用调用“Dispose”并释放您的资源,在发送您的电子邮件之前不要使用 Dispose,一切都会好起来的。
    【解决方案5】:

    将 using 放在 MailMessage 上,它将在内部处理构成附件的流

    using(var msg = new MailMessage())
    {
        ...
        ...
        var attachment = new Attachment(strMem, filename,contentType);
        msg.Attachments.Add(attachment);
        ...
        ...             
        msg.Send();
    }
    

    【讨论】:

    • 此代码不起作用。 MailMessage 类没有任何 Send() 方法。
    • 哈,你是对的。我的观点是 using 块,原始帖子已在 msg 上发送,这可能是 SmtpClient.Send(msg) 的 ex 方法
    最近更新 更多