如何使用 SmtpClient.SendAsync 发送带有附件的电子邮件?

Posted

技术标签:

【中文标题】如何使用 SmtpClient.SendAsync 发送带有附件的电子邮件?【英文标题】:How to send an email with attachments using SmtpClient.SendAsync? 【发布时间】:2010-09-21 18:02:48 【问题描述】:

我正在通过 ASP.NET MVC 使用服务组件。 我想以异步方式发送电子邮件,让用户无需等待发送就可以做其他事情。

当我发送没有附件的消息时,它工作正常。 当我发送带有至少一个内存附件的消息时,它会失败。

所以,我想知道是否可以使用带有内存附件的异步方法。

这里是发送方式


    public static void Send() 

        MailMessage message = new MailMessage("from@foo.com", "too@foo.com");
        using (MemoryStream stream = new MemoryStream(new byte[64000])) 
            Attachment attachment = new Attachment(stream, "my attachment");
            message.Attachments.Add(attachment);
            message.Body = "This is an async test.";

            SmtpClient smtp = new SmtpClient("localhost");
            smtp.Credentials = new NetworkCredential("foo", "bar");
            smtp.SendAsync(message, null);
        
    

这是我当前的错误


System.Net.Mail.SmtpException: Failure sending mail.
 ---> System.NotSupportedException: Stream does not support reading.
   at System.Net.Mime.MimeBasePart.EndSend(IAsyncResult asyncResult)
   at System.Net.Mail.Message.EndSend(IAsyncResult asyncResult)
   at System.Net.Mail.SmtpClient.SendMessageCallback(IAsyncResult result)
   --- End of inner exception stack trace ---

解决方案

    public static void Send()
    

            MailMessage message = new MailMessage("from@foo.com", "to@foo.com");
            MemoryStream stream = new MemoryStream(new byte[64000]);
            Attachment attachment = new Attachment(stream, "my attachment");
            message.Attachments.Add(attachment);
            message.Body = "This is an async test.";
            SmtpClient smtp = new SmtpClient("localhost");
            //smtp.Credentials = new NetworkCredential("login", "password");

            smtp.SendCompleted += delegate(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
            
                    if (e.Error != null)
                    
                            System.Diagnostics.Trace.TraceError(e.Error.ToString());

                    
                    MailMessage userMessage = e.UserState as MailMessage;
                    if (userMessage != null)
                    
                            userMessage.Dispose();
                    
            ;

            smtp.SendAsync(message, message);
    

【问题讨论】:

【参考方案1】:

我已经尝试过您的功能,它甚至适用于带有内存附件的电子邮件。但这里有一些说明:

您尝试发送什么类型的附件?可执行文件? 发件人和收件人是否在同一个电子邮件服务器上? 您应该“捕捉”异常,而不是仅仅吞下它,这样您才能获得有关您的问题的更多信息。

异常说明了什么?

使用 Send 而不是 SendAsync 是否有效?您正在使用 'using' 子句并在发送电子邮件之前关闭 Stream。

这是关于这个主题的好文章:

Sending Mail in .NET 2.0

【讨论】:

我应该添加更多代码,对此感到抱歉。让我编辑示例以提供更多信息。 在 VS 开发服务器中运行的异步调用是否可能实际上并不称为异步。我的模糊记忆试图记住某处所说的关于 VS Dev Web Server 是单线程的? 任何带有完整源代码示例的最终解决方案?【参考方案2】:

这里不要使用“使用”。您在调用 SendAsync 后立即销毁内存流,例如可能在 SMTP 读取它之前(因为它是异步的)。在回调中销毁您的流。

【讨论】:

谢谢。你救了我的命。 谢谢,你应该获得诺贝尔奖【参考方案3】:

对原始问题中提供的解决方案的扩展也可以正确清理可能也需要处理的附件。

    public event EventHandler EmailSendCancelled = delegate  ;

    public event EventHandler EmailSendFailure = delegate  ;

    public event EventHandler EmailSendSuccess = delegate  ;
    ...

        MemoryStream mem = new MemoryStream();
        try
        
            thisReport.ExportToPdf(mem);

            // Create a new attachment and put the PDF report into it.
            mem.Seek(0, System.IO.SeekOrigin.Begin);
            //Attachment att = new Attachment(mem, "MyOutputFileName.pdf", "application/pdf");
            Attachment messageAttachment = new Attachment(mem, thisReportName, "application/pdf");

            // Create a new message and attach the PDF report to it.
            MailMessage message = new MailMessage();
            message.Attachments.Add(messageAttachment);

            // Specify sender and recipient options for the e-mail message.
            message.From = new MailAddress(NOES.Properties.Settings.Default.FromEmailAddress, NOES.Properties.Settings.Default.FromEmailName);
            message.To.Add(new MailAddress(toEmailAddress, NOES.Properties.Settings.Default.ToEmailName));

            // Specify other e-mail options.
            //mail.Subject = thisReport.ExportOptions.Email.Subject;
            message.Subject = subject;
            message.Body = body;

            // Send the e-mail message via the specified SMTP server.
            SmtpClient smtp = new SmtpClient();
            smtp.SendCompleted += SmtpSendCompleted;
            smtp.SendAsync(message, message);
        
        catch (Exception)
        
            if (mem != null)
            
                mem.Dispose();
                mem.Close();
            
            throw;
        
    

    private void SmtpSendCompleted(object sender, AsyncCompletedEventArgs e)
    
        var message = e.UserState as MailMessage;
        if (message != null)
        
            foreach (var attachment in message.Attachments)
            
                if (attachment != null)
                
                    attachment.Dispose();
                
            
            message.Dispose();
        
        if (e.Cancelled)
            EmailSendCancelled?.Invoke(this, EventArgs.Empty);
        else if (e.Error != null)
        
            EmailSendFailure?.Invoke(this, EventArgs.Empty);
            throw e.Error;
        
        else
            EmailSendSuccess?.Invoke(this, EventArgs.Empty);
    

【讨论】:

以上是关于如何使用 SmtpClient.SendAsync 发送带有附件的电子邮件?的主要内容,如果未能解决你的问题,请参考以下文章

[精选] Mysql分表与分库如何拆分,如何设计,如何使用

如果加入条件,我该如何解决。如果使用字符串连接,我如何使用

如何使用本机反应创建登录以及如何验证会话

如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]

如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?

如何使用laravel保存所有行数据每个行名或相等