邮件服务是通常的系统都会拥有和须要的功能,可是对于.NET项目来讲,邮件服务的建立和使用会较为的麻烦。.NET对于邮件功能提供了System.Net.Mail用于建立邮件服务,该基础服务提供邮件的基础操做,而且使用也较为的简单。对于真正将该功能使用于项目的人,就会慢慢发现其中的优缺点,甚至有些时候不能忍受其中的问题。在这里介绍一种微软用于替代System.Net.Mail的邮件服务组件MailKit和MimeKit,官网地址:http://www.mimekit.net/。GitHub地址:https://github.com/jstedfast/MimeKit。下面就具体的介绍一下。html
MailKit组件的支持的客户端类型比较多,例如SMTP客户端、POP3客户端、IMAP4客户端。该组件是一个跨平台的Email组件,该组件支持.NET 4.0,.NET 4.5,Xamarin.Android,Xamarin.iOS,Windows Phone 8.1等等平台。git
MimeKit提供了一个MIME解析器,组件具有的解析特性灵活、性能高、很好的处理各类各样的破碎的MIME格式化。MimeKit的性能实际上与GMime至关。github
该组件在安全性的仍是比较高的,处理安全的方式较多,SASL认证、支持S / MIME v3.二、支持OpenPGP、支持DKIM签名等等方式。Mailkit组件能够经过CancellationToken取消对应的操做,CancellationToken传播应取消操做的通知,一个的CancellationToken使线程,线程池工做项目之间,或取消合做任务的对象。过实例化CancellationTokenSource对象来建立取消令牌,该对象管理从其CancellationTokenSource.Token属性检索的取消令牌。而后,将取消令牌传递到应该收到取消通知的任意数量的线程,任务或操做。令牌不能用于启动取消。安全
MailKit组件支持异步操做,在内部编写的有关I/O异步操做的类。服务器
介绍过MailKit和MimeKit组建的基础信息,接下来就介绍一下如何使用两个组件的基本功能,在这里我将基本操做作了一个简单的封装,通常的项目能够直接引用封装好的类,你们能够根据实际的状况对该组件进行扩展。app
/// <summary> /// 邮件服务API /// </summary> public static class MailServiceApi { /// <summary> /// 发送邮件 /// </summary> /// <param name="mailBodyEntity">邮件基础信息</param> /// <param name="sendServerConfiguration">发件人基础信息</param> public static SendResultEntity SendMail(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration) { if (sendServerConfiguration == null) { throw new ArgumentNullException(); } if (sendServerConfiguration == null) { throw new ArgumentNullException(); } var sendResultEntity = new SendResultEntity(); using (var client = new SmtpClient(new ProtocolLogger(CreateMailLog()))) { client.ServerCertificateValidationCallback = (s, c, h, e) => true; Connection(mailBodyEntity, sendServerConfiguration, client, sendResultEntity); if (sendResultEntity.ResultStatus == false) { return sendResultEntity; } SmtpClientBaseMessage(client); Authenticate(mailBodyEntity, sendServerConfiguration, client, sendResultEntity); if (sendResultEntity.ResultStatus == false) { return sendResultEntity; } Send(mailBodyEntity, sendServerConfiguration, client, sendResultEntity); if (sendResultEntity.ResultStatus == false) { return sendResultEntity; } client.Disconnect(true); } return sendResultEntity; } /// <summary> /// 链接服务器 /// </summary> /// <param name="mailBodyEntity">邮件内容</param> /// <param name="sendServerConfiguration">发送配置</param> /// <param name="client">客户端对象</param> /// <param name="sendResultEntity">发送结果</param> public static void Connection(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration, SmtpClient client, SendResultEntity sendResultEntity) { try { client.Connect(sendServerConfiguration.SmtpHost, sendServerConfiguration.SmtpPort); } catch (SmtpCommandException ex) { sendResultEntity.ResultInformation = $"尝试链接时出错:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } catch (SmtpProtocolException ex) { sendResultEntity.ResultInformation = $"尝试链接时的协议错误:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } catch (Exception ex) { sendResultEntity.ResultInformation = $"服务器链接错误:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } } /// <summary> /// 帐户认证 /// </summary> /// <param name="mailBodyEntity">邮件内容</param> /// <param name="sendServerConfiguration">发送配置</param> /// <param name="client">客户端对象</param> /// <param name="sendResultEntity">发送结果</param> public static void Authenticate(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration, SmtpClient client, SendResultEntity sendResultEntity) { try { client.Authenticate(sendServerConfiguration.SenderAccount, sendServerConfiguration.SenderPassword); } catch (AuthenticationException ex) { sendResultEntity.ResultInformation = $"无效的用户名或密码:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } catch (SmtpCommandException ex) { sendResultEntity.ResultInformation = $"尝试验证错误:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } catch (SmtpProtocolException ex) { sendResultEntity.ResultInformation = $"尝试验证时的协议错误:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } catch (Exception ex) { sendResultEntity.ResultInformation = $"帐户认证错误:{0}" + ex.Message; sendResultEntity.ResultStatus = false; } } /// <summary> /// 发送邮件 /// </summary> /// <param name="mailBodyEntity">邮件内容</param> /// <param name="sendServerConfiguration">发送配置</param> /// <param name="client">客户端对象</param> /// <param name="sendResultEntity">发送结果</param> public static void Send(MailBodyEntity mailBodyEntity, SendServerConfigurationEntity sendServerConfiguration, SmtpClient client, SendResultEntity sendResultEntity) { try { client.Send(MailMessage.AssemblyMailMessage(mailBodyEntity)); } catch (SmtpCommandException ex) { switch (ex.ErrorCode) { case SmtpErrorCode.RecipientNotAccepted: sendResultEntity.ResultInformation = $"收件人未被接受:{ex.Message}"; break; case SmtpErrorCode.SenderNotAccepted: sendResultEntity.ResultInformation = $"发件人未被接受:{ex.Message}"; break; case SmtpErrorCode.MessageNotAccepted: sendResultEntity.ResultInformation = $"消息未被接受:{ex.Message}"; break; } sendResultEntity.ResultStatus = false; } catch (SmtpProtocolException ex) { sendResultEntity.ResultInformation = $"发送消息时的协议错误:{ex.Message}"; sendResultEntity.ResultStatus = false; } catch (Exception ex) { sendResultEntity.ResultInformation = $"邮件接收失败:{ex.Message}"; sendResultEntity.ResultStatus = false; } } /// <summary> /// 获取SMTP基础信息 /// </summary> /// <param name="client">客户端对象</param> /// <returns></returns> public static MailServerInformation SmtpClientBaseMessage(SmtpClient client) { var mailServerInformation = new MailServerInformation { Authentication = client.Capabilities.HasFlag(SmtpCapabilities.Authentication), BinaryMime = client.Capabilities.HasFlag(SmtpCapabilities.BinaryMime), Dsn = client.Capabilities.HasFlag(SmtpCapabilities.Dsn), EightBitMime = client.Capabilities.HasFlag(SmtpCapabilities.EightBitMime), Size = client.MaxSize }; return mailServerInformation; } /// <summary> /// 建立邮件日志文件 /// </summary> /// <returns></returns> public static string CreateMailLog() { var logPath = AppDomain.CurrentDomain.BaseDirectory + "/DocumentLog/" + Guid.NewGuid() + ".txt"; if (File.Exists(logPath)) return logPath; var fs = File.Create(logPath); fs.Close(); return logPath; } }
/// <summary> /// 邮件信息 /// </summary> public static class MailMessage { /// <summary> /// 组装邮件文本/附件邮件信息 /// </summary> /// <param name="mailBodyEntity">邮件消息实体</param> /// <returns></returns> public static MimeMessage AssemblyMailMessage(MailBodyEntity mailBodyEntity) { if (mailBodyEntity == null) { throw new ArgumentNullException(nameof(mailBodyEntity)); } var message = new MimeMessage(); //设置邮件基本信息 SetMailBaseMessage(message, mailBodyEntity); var multipart = new Multipart("mixed"); //插入文本消息 if (string.IsNullOrEmpty(mailBodyEntity.MailTextBody) == false) { var alternative = new MultipartAlternative { AssemblyMailTextMessage(mailBodyEntity.MailTextBody, mailBodyEntity.MailBodyType) }; multipart.Add(alternative); } //插入附件 if (mailBodyEntity.MailFilePath != null && File.Exists(mailBodyEntity.MailFilePath) == false) { var mimePart = AssemblyMailAttachmentMessage(mailBodyEntity.MailFileType, mailBodyEntity.MailFileSubType, mailBodyEntity.MailFilePath); multipart.Add(mimePart); } //组合邮件内容 message.Body = multipart; return message; } /// <summary> /// 设置邮件基础信息 /// </summary> /// <param name="minMessag"></param> /// <param name="mailBodyEntity"></param> /// <returns></returns> public static MimeMessage SetMailBaseMessage(MimeMessage minMessag, MailBodyEntity mailBodyEntity) { if (minMessag == null) { throw new ArgumentNullException(); } if (mailBodyEntity == null) { throw new ArgumentNullException(); } //插入发件人 minMessag.From.Add(new MailboxAddress(mailBodyEntity.Sender, mailBodyEntity.SenderAddress)); //插入收件人 foreach (var recipients in mailBodyEntity.Recipients) { minMessag.To.Add(new MailboxAddress(recipients)); } //插入抄送人 foreach (var cC in mailBodyEntity.Cc) { minMessag.Cc.Add(new MailboxAddress(cC)); } //插入主题 minMessag.Subject = mailBodyEntity.Subject; return minMessag; } /// <summary> /// 组装邮件文本信息 /// </summary> /// <param name="mailBody">邮件文本内容</param> /// <param name="textPartType">邮件文本类型(plain,html,rtf,xml)</param> /// <returns></returns> public static TextPart AssemblyMailTextMessage(string mailBody, string textPartType) { if (string.IsNullOrEmpty(mailBody)) { throw new ArgumentNullException(); } if (string.IsNullOrEmpty(textPartType)) { throw new ArgumentNullException(); } var textBody = new TextPart(textPartType) { Text = mailBody }; return textBody; } /// <summary> /// 组装邮件附件信息 /// </summary> /// <param name="fileAttachmentType">附件类型(image,application)</param> /// <param name="fileAttachmentSubType">附件子类型 </param> /// <param name="fileAttachmentPath">附件路径</param> /// <returns></returns> public static MimePart AssemblyMailAttachmentMessage(string fileAttachmentType, string fileAttachmentSubType, string fileAttachmentPath) { if (string.IsNullOrEmpty(fileAttachmentSubType)) { throw new ArgumentNullException(); } if (string.IsNullOrEmpty(fileAttachmentType)) { throw new ArgumentNullException(); } if (string.IsNullOrEmpty(fileAttachmentPath)) { throw new ArgumentNullException(); } var attachment = new MimePart(fileAttachmentType, fileAttachmentSubType) { Content = new MimeContent(File.OpenRead(fileAttachmentPath)), ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), ContentTransferEncoding = ContentEncoding.Base64, FileName = Path.GetFileName(fileAttachmentPath) }; return attachment; } }
/// <summary> /// 邮件内容实体 /// </summary> public class MailBodyEntity { /// <summary> /// 邮件文本内容 /// </summary> public string MailTextBody { get; set; } /// <summary> /// 邮件内容类型 /// </summary> public string MailBodyType { get; set; } /// <summary> /// 邮件附件文件类型 /// </summary> public string MailFileType { get; set; } /// <summary> /// 邮件附件文件子类型 /// </summary> public string MailFileSubType { get; set; } /// <summary> /// 邮件附件文件路径 /// </summary> public string MailFilePath { get; set; } /// <summary> /// 收件人 /// </summary> public List<string> Recipients { get; set; } /// <summary> /// 抄送 /// </summary> public List<string> Cc { get; set; } /// <summary> /// 发件人 /// </summary> public string Sender { get; set; } /// <summary> /// 发件人地址 /// </summary> public string SenderAddress { get; set; } /// <summary> /// 邮件主题 /// </summary> public string Subject { get; set; } /// <summary> /// 邮件内容 /// </summary> public string Body { get; set; } } /// <summary> /// 邮件服务器基础信息 /// </summary> public class MailServerInformation { /// <summary> /// SMTP服务器支持SASL机制类型 /// </summary> public bool Authentication { get; set; } /// <summary> /// SMTP服务器对消息的大小 /// </summary> public uint Size { get; set; } /// <summary> /// SMTP服务器支持传递状态通知 /// </summary> public bool Dsn { get; set; } /// <summary> /// SMTP服务器支持Content-Transfer-Encoding /// </summary> public bool EightBitMime { get; set; } /// <summary> /// SMTP服务器支持Content-Transfer-Encoding /// </summary> public bool BinaryMime { get; set; } /// <summary> /// SMTP服务器在消息头中支持UTF-8 /// </summary> public string UTF8 { get; set; } } /// <summary> /// 邮件发送结果 /// </summary> public class SendResultEntity { /// <summary> /// 结果信息 /// </summary> public string ResultInformation { get; set; } = "发送成功!"; /// <summary> /// 结果状态 /// </summary> public bool ResultStatus { get; set; } = true; } /// <summary> /// 邮件发送服务器配置 /// </summary> public class SendServerConfigurationEntity { /// <summary> /// 邮箱SMTP服务器地址 /// </summary> public string SmtpHost { get; set; } /// <summary> /// 邮箱SMTP服务器端口 /// </summary> public int SmtpPort { get; set; } /// <summary> /// 是否启用IsSsl /// </summary> public bool IsSsl { get; set; } /// <summary> /// 邮件编码 /// </summary> public string MailEncoding { get; set; } /// <summary> /// 发件人帐号 /// </summary> public string SenderAccount { get; set; } /// <summary> /// 发件人密码 /// </summary> public string SenderPassword { get; set; } }
上面提供了借助MailKit组建建立发送邮件服务,分别是建立邮件服务器链接,组装邮件基础信息,邮件基础实体。发送邮件的基础服务比较的多,下面介绍一下邮件的接收。异步
/// <summary> /// 跟投邮件服务API /// </summary> public static class ReceiveEmailServiceApi { /// <summary> /// 设置发件人信息 /// </summary> /// <returns></returns> public static SendServerConfigurationEntity SetSendMessage() { var sendServerConfiguration = new SendServerConfigurationEntity { SmtpHost = ConfigurationManager.AppSettings["SmtpServer"], SmtpPort = int.Parse(ConfigurationManager.AppSettings["SmtpPort"]), IsSsl = Convert.ToBoolean(ConfigurationManager.AppSettings["IsSsl"]), MailEncoding = ConfigurationManager.AppSettings["MailEncoding"], SenderAccount = ConfigurationManager.AppSettings["SenderAccount"], SenderPassword = ConfigurationManager.AppSettings["SenderPassword"] }; return sendServerConfiguration; } /// <summary> /// 接收邮件 /// </summary> public static void ReceiveEmail() { var sendServerConfiguration = SetSendMessage(); if (sendServerConfiguration == null) { throw new ArgumentNullException(); } using (var client = new ImapClient(new ProtocolLogger(CreateMailLog()))) { client.Connect(sendServerConfiguration.SmtpHost, sendServerConfiguration.SmtpPort, SecureSocketOptions.SslOnConnect); client.Authenticate(sendServerConfiguration.SenderAccount, sendServerConfiguration.SenderPassword); client.Inbox.Open(FolderAccess.ReadOnly); var uids = client.Inbox.Search(SearchQuery.All); foreach (var uid in uids) { var message = client.Inbox.GetMessage(uid); message.WriteTo($"{uid}.eml"); } client.Disconnect(true); } } /// <summary> /// 下载邮件内容 /// </summary> public static void DownloadBodyParts() { var sendServerConfiguration = SetSendMessage(); using (var client = new ImapClient()) { client.Connect(sendServerConfiguration.SmtpHost, sendServerConfiguration.SmtpPort, SecureSocketOptions.SslOnConnect); client.Authenticate(sendServerConfiguration.SenderAccount, sendServerConfiguration.SenderPassword); client.Inbox.Open(FolderAccess.ReadOnly); // 搜索Subject标题包含“MimeKit”或“MailKit”的邮件 var query = SearchQuery.SubjectContains("MimeKit").Or(SearchQuery.SubjectContains("MailKit")); var uids = client.Inbox.Search(query); // 获取搜索结果的摘要信息(咱们须要UID和BODYSTRUCTURE每条消息,以便咱们能够提取文本正文和附件) var items = client.Inbox.Fetch(uids, MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure); foreach (var item in items) { // 肯定一个目录来保存内容 var directory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "/MailBody", item.UniqueId.ToString()); Directory.CreateDirectory(directory); // IMessageSummary.TextBody是一个便利的属性,能够为咱们找到“文本/纯文本”的正文部分 var bodyPart = item.TextBody; // 下载'text / plain'正文部分 var body = (TextPart) client.Inbox.GetBodyPart(item.UniqueId, bodyPart); // TextPart.Text是一个便利的属性,它解码内容并将结果转换为咱们的字符串 var text = body.Text; File.WriteAllText(Path.Combine(directory, "body.txt"), text); // 如今遍历全部附件并将其保存到磁盘 foreach (var attachment in item.Attachments) { // 像咱们对内容所作的那样下载附件 var entity = client.Inbox.GetBodyPart(item.UniqueId, attachment); // 附件能够是message / rfc822部件或常规MIME部件 var messagePart = entity as MessagePart; if (messagePart != null) { var rfc822 = messagePart; var path = Path.Combine(directory, attachment.PartSpecifier + ".eml"); rfc822.Message.WriteTo(path); } else { var part = (MimePart) entity; // 注意:这多是空的,但大多数会指定一个文件名 var fileName = part.FileName; var path = Path.Combine(directory, fileName); // decode and save the content to a file using (var stream = File.Create(path)) part.Content.DecodeTo(stream); } } } client.Disconnect(true); } } /// <summary> /// 建立邮件日志文件 /// </summary> /// <returns></returns> public static string CreateMailLog() { var logPath = AppDomain.CurrentDomain.BaseDirectory + "/DocumentLog/" + DateTime.Now.ToUniversalTime().ToString(CultureInfo.InvariantCulture) + ".txt"; if (File.Exists(logPath)) return logPath; var fs = File.Create(logPath); fs.Close(); return logPath; } }
上面只是简单的介绍了邮件的接收,若是须要更加深刻的了解功能,能够进一步对组件源码进行解析,该组件的文档为较为的丰富。性能
MailKit和MimeKit组件在项目的使用中较为的便捷,基本包含了全部的基础邮件服务操做。组件提供的SmtpClient类提供的功能很丰富,例如链接邮件服务器,邮件帐户认证,组装邮件消息,获取邮件服务器配置信息等等方法的提供,可让咱们在项目中快速的获取邮件服务的全部信息。ui
使用过邮件功能的项目 都会有困扰,客户端与邮件服务器的链接是否成功,以及邮件是否发送成功状态没有办法很快的获取,只能根据邮件服务器返回的一场状态进行判断。可是MailKit提供对应的方法和异常类,对邮件服务器返回的异常信息进行解析,客户端能够根据这些异常类获取邮件状态。编码
MailKit组件的提供了ProtocolLogger类,该类用于记录SMTP操做基础信息,该类做用为记录邮件服务日志。在邮件发送完毕后,须要及时的关闭链接,调用Disconnect(true)方法。