接下来就是方法SendMail了:
private void SendMail(MailBox box) { if (box == null) { throw new ArgumentNullException(nameof(box)); } try { MimeMessage message = ConvertToMimeMessage(box); SendMail(message); } catch (Exception exception) { _logger.LogError(exception, "发送邮件发生异常主题:{0},收件人:{1}", box.Subject, box.To.First()); } finally { if (box.Attachments != null && box.Attachments.Any()) { foreach (var item in box.Attachments) { item.Dispose(); ...这里有一个特别要注意的就是在发送之后释放附件(非托管资源):
foreach (var item in box.Attachments) { item.Dispose(); ...发送邮件的核心代码只有两行:
MimeMessage message = ConvertToMimeMessage(box); SendMail(message);第一行将mailbox转换成 MailKit使用的MimeMessage实体,第二步切实的发送邮件
为什么,我们的接口中没有直接使用MimeMessage而是使用MailBox?
因为MimeMessage比较繁杂,而且附件的问题不易处理,所以我们设计接口时单独封装MailBox简化了编程接口
转换一共两步,1是主体转换,比较简单。二是附件的处理这里涉及到附件名中文编码的问题。
private MimeMessage ConvertToMimeMessage(MailBox box) { var message = new MimeMessage(); var from = InternetAddress.Parse(_options.UserName); from.Name = _options.DisplayName; message.From.Add(from); if (!box.To.Any()) { throw new ArgumentNullException("to必须含有值"); } message.To.AddRange(box.To.Convert()); if (box.Cc != null && box.Cc.Any()) { message.Cc.AddRange(box.Cc.Convert()); } message.Subject = box.Subject; var builder = new BodyBuilder(); if (box.IsHtml) { builder.HtmlBody = box.Body; } else { builder.TextBody = box.Body; } if (box.Attachments != null && box.Attachments.Any()) { foreach (var item in GetAttechments(box.Attachments)) { builder.Attachments.Add(item); } } message.Body = builder.ToMessageBody(); return message; } private AttachmentCollection GetAttechments(IEnumerable<IAttachment> attachments) { if (attachments == null) { throw new ArgumentNullException(nameof(attachments)); } AttachmentCollection collection = new AttachmentCollection(); List<Stream> list = new List<Stream>(attachments.Count()); foreach (var item in attachments) { var fileName = item.GetName(); var fileType = MimeTypes.GetMimeType(fileName); var contentTypeArr = fileType.Split('http://www.likecs.com/'); var contentType = new ContentType(contentTypeArr[0], contentTypeArr[1]); MimePart attachment = null; Stream fs = null; try { fs = item.GetFileStream(); list.Add(fs); } catch (Exception ex) { _logger.LogError(ex, "读取文件流发生异常"); fs?.Dispose(); continue; } attachment = new MimePart(contentType) { Content = new MimeContent(fs), ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), ContentTransferEncoding = ContentEncoding.Base64, }; var charset = "UTF-8"; attachment.ContentType.Parameters.Add(charset, "name", fileName); attachment.ContentDisposition.Parameters.Add(charset, "filename", fileName); foreach (var param in attachment.ContentDisposition.Parameters) { param.EncodingMethod = ParameterEncodingMethod.Rfc2047; } foreach (var param in attachment.ContentType.Parameters) { param.EncodingMethod = ParameterEncodingMethod.Rfc2047; } collection.Add(attachment); } return collection; }在转化附件时下面的代码用来处理附件名编码问题:
var charset = "UTF-8"; attachment.ContentType.Parameters.Add(charset, "name", fileName); attachment.ContentDisposition.Parameters.Add(charset, "filename", fileName); foreach (var param in attachment.ContentDisposition.Parameters) { param.EncodingMethod = ParameterEncodingMethod.Rfc2047; } foreach (var param in attachment.ContentType.Parameters) { param.EncodingMethod = ParameterEncodingMethod.Rfc2047; }到这了我们的邮件队列就基本完成了,接下来就是在程序启动后,启动队列,找到 Program.cs文件,并稍作改写如下:
var host = BuildWebHost(args); var provider = host.Services; provider.GetRequiredService<IMailQueueManager>().Run(); host.Run();这里在host.Run()主机启动之前,我们获取了IMailQueueManager并启动队列(别忘了注册服务)。