PdfGenerator EmailMessage EmailAttachment - 无法访问已关闭的流



尝试将System.IO.MemoryStreamEmailAttachment附加到EmailMessage时,抛出带有消息Cannot access a closed Stream的错误System.ObjectDisposedException。附件是使用 HtmlRenderer.PdfSharp 生成的。附加和发送是在 using 语句中完成的。

using (MemoryStream ms = new MemoryStream())
{
var pdf = PdfGenerator.GeneratePdf("<html><body>foo</body></html>", PdfSharp.PageSize.Letter);
// 2nd argument is to NOT close stream
pdf.Save(ms, false);
EmailMessage em = new EmailMessage();
em.EmailFormat = EmailFormatEnum.Html;
em.From = "no-reply@foo.com";
em.Recipients = "foo.bar@baz.net";
em.Subject = "Attachment Name";
em.Body = "There is an attachment";
var attachment = new EmailAttachment(ms, "foo.pdf");
em.Attachments.Add(attachment);
// SiteContext.CurrentSiteName argument is just a site name required for Kentico CMS
// true argument is send immediately
EmailSender.SendEmail(SiteContext.CurrentSiteName, em, true);    
}

如果我改为执行以下操作,则只要我不尝试在try块内ms.Dispose(),错误就不会发生:

MemoryStream ms = new MemoryStream();
try
{
var pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
pdf.Save(ms, false);
EmailMessage em = new EmailMessage();
em.EmailFormat = EmailFormatEnum.Html;
em.From = "no-reply@foo.com";
em.Recipients = "foo.bar@baz.net";
em.Subject = "Attachment Name";
em.Body = "There is an attachment";
var attachment = new EmailAttachment(ms, "foo.pdf");
em.Attachments.Add(attachment);
EmailSender.SendEmail(SiteContext.CurrentSiteName, em, true);
}
catch (Exception)
{
ms.Dispose();
}

第二种方法的问题是MemoryStream没有被显式处置。MemoryStream是否天生就被采用这种方法?如果放在整个块之后ms.Dispose();,则会发生相同的Cannot access a closed Stream。这是等待发生的内存泄漏吗?如何在不关闭流以允许电子邮件发送附件的情况下处置MemoryStream

这是需要以某种方式等待SendEmail()实际执行其功能的问题吗?

感谢您提供的任何帮助。

  1. 我不明白你为什么要在try块中处理ms,通常是为了清理变量,你使用finally 块,无论是否有异常,它都会被执行。

  2. 如果您使用 catch(那种带有 Exception 的捕获会捕获所有异常(,则不会收到错误,因为您正在捕获它们

你应该怎么做是:

MemoryStream ms = new MemoryStream();
try
{
var pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.A4);
pdf.Save(ms, false);
EmailMessage em = new EmailMessage();
em.EmailFormat = EmailFormatEnum.Html;
em.From = "no-reply@foo.com";
em.Recipients = "foo.bar@baz.net";
em.Subject = "Attachment Name";
em.Body = "There is an attachment";
var attachment = new EmailAttachment(ms, "foo.pdf");
em.Attachments.Add(attachment);
EmailSender.SendEmail(SiteContext.CurrentSiteName, em, true);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
ms.Dispose();
}

我能够使用以下代码找到解决方案。关于SendMail()论点,sendImmediately这实际上可能更像是肯蒂科CMS的问题。通过将其设置为false(默认值(,我能够将流附加为EmailAttachment,而不会using语句中出现System.ObjectDisposedException错误。

using (MemoryStream ms = new MemoryStream())
{
var pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.Letter);
pdf.Save(ms, false);
EmailMessage em = new EmailMessage();
em.EmailFormat = EmailFormatEnum.Html;
em.From = "no-reply@foo.com";
em.Recipients = "foo.bar@baz.net";
em.Subject = "Attachment Name";
em.Body = "There is an attachment.";
var attachment = new EmailAttachment(ms, "foo.pdf");
em.Attachments.Add(attachment);
// default sendImmediately of false
EmailSender.SendEmail(SiteContext.CurrentSiteName, em);
}

好吧,您只需要在从 Kentico 访问之前关闭流,即:

using (MemoryStream ms = new MemoryStream())
{
var pdf = PdfGenerator.GeneratePdf(html, PdfSharp.PageSize.Letter);
pdf.Save(ms, false);
}

然后你做你的电子邮件部分:

EmailMessage em = new EmailMessage();
em.EmailFormat = EmailFormatEnum.Html;
em.From = "no-reply@foo.com";
em.Recipients = "foo.bar@baz.net";
em.Subject = "Attachment Name";
em.Body = "There is an attachment.";
using (MemoryStream ms = new MemoryStream())
{
var attachment = new EmailAttachment(ms, "foo.pdf");
em.Attachments.Add(attachment);
}
EmailSender.SendEmail(SiteContext.CurrentSiteName, em);

最新更新