发起的.NET Core开源组织号召,进展的速度是我本身也没有想到的,不少园友都积极参与(虽然有些人诚心砸场子,要是以我之前的宝脾气,这会应该被我打住院了吧,不过幸亏是少数,作一件事总有人说好,也有人说是用武汉话说“闹眼子”),.NET社区不是没有乐于共享知识的人,只是没有一个完整和良好的生态环境,总之但愿国内的.NET发展愈来愈强大。我在这里想到一句话“咱们但愿本身能够作巨浪,但咱们也甘愿作巨浪来袭前的小浪”。程序员
上面扯淡完毕(我这人干正事前,都要将一些扯淡的话,这个习惯改不掉了...)安全
项目中为了及时的通讯,有直接发数据到页面,也有利用短信通知,也有我门今天介绍的邮件组件。咱们今天的主要任务就是讲解一下有一个.NET的免费开源的邮件组件MailKit。本文将一如既往的结合实例和组件底层代码讲解一下相关组件的知识。(项目招人的时候,我都会问一下.NET的底层原理,有一个大神问我这样有什么意义吗?咱们也写不出.NET底层那样的优秀处理方式,为什么取了解这些,其实我我的以为,问底层的原理,只是向为了跟好的处理一些程序出现的问题,以及对程序编码的时候,选择最合适的方式提高性能,任何一种方式都有优点和劣势,.NET的类库代码也是如此,若是咱们知道.NET的底层实现,咱们在项目的需求实现时,能够根据.NET底层实现,选择合适的方式,以求性能最优)。异步
项目中使用Email的操做机会比较多,通常稍微大一点的项目,都会使用到邮件操做这一个操做。对于.NET邮件操做的组件和方式比较多,今天咱们就介绍一款邮件操做的组件MailKit,这个邮件组件是一个开源免费的,咱们如今就来了解一下这一个组件的特色。MimeKit旨在经过尽量接近地遵循MIME规范来解决这个问题,同时还为程序员提供了一个很是容易使用的高级API。性能
组件的支持的客户端类型比较多,例如SMTP客户端、POP3客户端、IMAP客户端。该组件是一个跨平台的Email组件,该组件支持.NET 4.0,.NET 4.5,Xamarin.Android,Xamarin.iOS,Windows Phone 8.1等等平台。该组件提供了一个MIME解析器,组件具有的解析特性灵活、性能高、很好的处理各类各样的破碎的MIME格式化。MimeKit的性能实际上与GMime至关。学习
该组件在安全性的仍是比较高的,处理安全的方式较多,SASL认证、支持S / MIME v3.二、支持OpenPGP、支持DKIM签名等等方式。Mailkit组件能够经过CancellationToken取消对应的操做,CancellationToken传播应取消操做的通知,一个的CancellationToken使线程,线程池工做项目之间,或取消合做任务的对象。过实例化CancellationTokenSource对象来建立取消令牌,该对象管理从其CancellationTokenSource.Token属性检索的取消令牌。而后,将取消令牌传递到应该收到取消通知的任意数量的线程,任务或操做。令牌不能用于启动取消。编码
MailKit组件支持异步操做,在内部编写的有关I/O异步操做的类。spa
上面介绍了MailKit组件的背景和特色,这里就介绍一下Email组件的简单应用。线程
public void SentEmail(string path) { var message = new MimeMessage(); //获取From标头中的地址列表,添加指定的地址 message.From.Add(new MailboxAddress("Joey", "joey@friends.com")); //获取To头中的地址列表,添加指定的地址 message.To.Add(new MailboxAddress("Alice", "alice@wonderland.com")); //获取或设置消息的主题 message.Subject = "How you doin?"; // 建立咱们的消息文本,就像之前同样(除了不设置为message.Body) var body = new TextPart("plain") { Text = @"Hey Alice-- Joey" }; // 为位于路径的文件建立图像附件 var attachment = new MimePart("image", "gif") { ContentObject = new ContentObject(File.OpenRead(path), ContentEncoding.Default), ContentDisposition = new ContentDisposition(ContentDisposition.Attachment), ContentTransferEncoding = ContentEncoding.Base64, FileName = Path.GetFileName(path) }; // 如今建立multipart / mixed容器来保存消息文本和图像附件 var multipart = new Multipart("mixed") { body, attachment }; // 如今将multipart / mixed设置为消息正文 message.Body = multipart; }
调用该组件发送邮件和为邮件添加附件是比较简单的,第一步是实例化MimeMessage对象,对于该对象的解析将在下面进行,获得MimeMessage对象后,指定邮件的地址和主题等等相关信息。第二步实例化TextPart对象,为对象设定文本信息。若须要问邮件建立文件的附件,可使用MimePart对象,包含内容(如消息正文文本或)的叶节点MIME部分一个附件。第四步为建立的邮件主体和文本以及附件信息后,能够建立Multipart对象,建立邮件容器,用来装载文本信息和附件。最后调用MimeMessage.body属性获取或设置消息的正文。调试
var message = MimeMessage.Load(stream);
邮件的信息咱们须要进行对应的解析,这里咱们使用MimeMessage的Load方法,该方法从指定的流加载MimeKit.MimeMessage。另外一个加载数据的方式,可使用MimeParser类,这里就再也不解析了。code
public static void HandleMimeEntity(MimeEntity entity) { //MimeEntity转化为Multipart实体 var multipart = entity as Multipart; if (multipart != null) { for (int i = 0; i < multipart.Count; i++) HandleMimeEntity(multipart[i]); return; } var rfc822 = entity as MessagePart; if (rfc822 != null) { var message = rfc822.Message; HandleMimeEntity(message.Body); return; } var part = (MimePart)entity; }
以上是对接收到的消息的一个遍历,采用递归遍历MIME结构。MIME是内容的树结构,很像一个文件系统。MIME确实定义了一组通用规则,用于邮件客户端如何解释MIME部分的树结构。的 内容处置头是为了给接收客户端提供提示以哪些部分是为了显示做为消息体的一部分,而且意在被解释为附件。另外两种方式这离就不作介绍了。
上面介绍了Email的基本操做就不作过多的介绍,在使用该组件时,较为的简单。这里就来看看该组件的类型结构和一些核心对象。类库结构有以下图:
public static MimeMessage Load (ParserOptions options, Stream stream, bool persistent,
CancellationToken cancellationToken = default (CancellationToken)) { if (options == null) throw new ArgumentNullException (nameof (options)); if (stream == null) throw new ArgumentNullException (nameof (stream)); var parser = new MimeParser (options, stream, MimeFormat.Entity, persistent); return parser.ParseMessage (cancellationToken); }
该方法从指定的流加载MimeMessage,具备6个方法重载。该方法返回一个MimeMessage对象,有源码能够看出,在该方法内部建立了一个MimeParser对象,MimeParser包含内容(例如邮件正文文本或附件)的叶节点MIME部分。调用ParseMessage方法,解析来自流的消息。
2.TextPart.Text:
public string Text { get { if (ContentObject == null) return string.Empty; var charset = ContentType.Parameters["charset"]; using (var memory = new MemoryStream ()) { ContentObject.DecodeTo (memory); var content = memory.ToArray (); Encoding encoding = null; if (charset != null) { try { encoding = CharsetUtils.GetEncoding (charset); } catch (NotSupportedException) { } } if (encoding == null) { try { return CharsetUtils.UTF8.GetString (content, 0, (int) memory.Length); } catch (DecoderFallbackException) { encoding = CharsetUtils.Latin1; } } return encoding.GetString (content, 0, (int) memory.Length); } } set { SetText (Encoding.UTF8, value); } }
该属性获取解码的文本内容。该属性是一个可读可写的属性。ContentType.Parameters["charset"]用于获取charset参数的值。该方法用来将参数的值设置为数据流并设置对应的编码。看到这里的异常处理结构,就想简单的谈几句,.NET的异常比较的薄弱,不少时候在写.NET的异常时就更加的简单,以上是对异常知识捕获,有些地方并无作处理,有些地方是对异常的地方进行恢复。
public virtual void WriteTo (FormatOptions options, Stream stream, bool contentOnly,
CancellationToken cancellationToken = default (CancellationToken)) { if (options == null) throw new ArgumentNullException (nameof (options)); if (stream == null) throw new ArgumentNullException (nameof (stream)); if (!contentOnly) Headers.WriteTo (options, stream, cancellationToken); }
该方法将MimeEntity写入到指定的数据流中,该方法接受参数options格式选项。stream输出数据流,contentOnly判断是否可写。该方法定义为虚方法,在继承此方法后,能够在子类种对该方法进行重写。
本人以为在项目开发中,若是引入了第三方组件,咱们尽可能引入组件的源码,这样咱们对整个组件的结构有一个认识,组件的实现方式咱们也能够进行细致了解,尤为是咱们在进行调试的过后更加有用,咱们能够逐一的进行断点调试。以上是对该组件的一个简单介绍,有兴趣的能够去深刻的了解和学习。