發起的.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判斷是否可寫。該方法定義爲虛方法,在繼承此方法後,能夠在子類種對該方法進行重寫。
本人以爲在項目開發中,若是引入了第三方組件,咱們儘可能引入組件的源碼,這樣咱們對整個組件的結構有一個認識,組件的實現方式咱們也能夠進行細緻瞭解,尤爲是咱們在進行調試的過後更加有用,咱們能夠逐一的進行斷點調試。以上是對該組件的一個簡單介紹,有興趣的能夠去深刻的瞭解和學習。