跨平台的.NET邮件协议MailKit组件解析
發起的.NET Core開源組織號召,進展的速度是我自己也沒有想到的,很多園友都積極參與(雖然有些人誠心砸場子,要是以我以前的寶脾氣,這會應該被我打住院了吧,不過幸好是少數,做一件事總有人說好,也有人說是用武漢話說“鬧眼子”),.NET社區不是沒有樂于共享知識的人,只是沒有一個完整和良好的生態環境,總之希望國內的.NET發展越來越強大。我在這里想到一句話“我們希望自己可以做巨浪,但我們也甘愿做巨浪來襲前的小浪”。
? ?上面扯淡完畢(我這人干正事前,都要將一些扯淡的話,這個習慣改不掉了...)
? ?項目中為了及時的通信,有直接發數據到頁面,也有利用短信通知,也有我門今天介紹的郵件組件。我們今天的主要任務就是講解一下有一個.NET的免費開源的郵件組件MailKit。本文將一如既往的結合實例和組件底層代碼講解一下相關組件的知識。(項目招人的時候,我都會問一下.NET的底層原理,有一個大神問我這樣有什么意義嗎?我們也寫不出.NET底層那樣的優秀處理方式,為何取了解這些,其實我個人覺得,問底層的原理,只是向為了跟好的處理一些程序出現的問題,以及對程序編碼的時候,選擇最合適的方式提升性能,任何一種方式都有優勢和劣勢,.NET的類庫代碼也是如此,如果我們知道.NET的底層實現,我們在項目的需求實現時,可以根據.NET底層實現,選擇合適的方式,以求性能最優)。
一.Mailkit組件概述
? ?項目中使用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.2、支持OpenPGP、支持DKIM簽名等等方式。Mailkit組件可以通過CancellationToken取消對應的操作,CancellationToken傳播應取消操作的通知,一個的CancellationToken使線程,線程池工作項目之間,或取消合作任務的對象。過實例化CancellationTokenSource對象來創建取消令牌,該對象管理從其CancellationTokenSource.Token屬性檢索的取消令牌。然后,將取消令牌傳遞到應該收到取消通知的任意數量的線程,任務或操作。令牌不能用于啟動取消。
??MailKit組件支持異步操作,在內部編寫的有關I/O異步操作的類。
二.MailKit實例:
? ? 上面介紹了MailKit組件的背景和特點,這里就介紹一下Email組件的簡單應用。
? 1.創建郵件方式:
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屬性獲取或設置消息的正文。
? ? 2.郵件信息的解析:
var message = MimeMessage.Load(stream);? ?郵件的信息我們需要進行對應的解析,這里我們使用MimeMessage的Load方法,該方法從指定的流加載MimeKit.MimeMessage。另一個加載數據的方式,可以使用MimeParser類,這里就不再解析了。
? ? 3.郵件的接收:
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部分的樹結構。的?內容處置頭是為了給接收客戶端提供提示以哪些部分是為了顯示作為消息體的一部分,并且意在被解釋為附件。另外兩種方式這離就不做介紹了。
三.MailKit核心對象解析
? ?上面介紹了Email的基本操作就不做過多的介紹,在使用該組件時,較為的簡單。這里就來看看該組件的類型結構和一些核心對象。類庫結構有如下圖:
? ? 1.MimeMessage.Load():
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的異常時就更加的簡單,以上是對異常知識捕獲,有些地方并沒有做處理,有些地方是對異常的地方進行恢復。
? ?3.MimeEntity.WriteTo():
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判斷是否可寫。該方法定義為虛方法,在繼承此方法后,可以在子類種對該方法進行重寫。
四.總結
? ?本人覺得在項目開發中,如果引入了第三方組件,我們盡量引入組件的源碼,這樣我們對整個組件的結構有一個認識,組件的實現方式我們也可以進行細致了解,尤其是我們在進行調試的事后更加有用,我們可以逐一的進行斷點調試。以上是對該組件的一個簡單介紹,有興趣的可以去深入的了解和學習。
原文地址:http://www.cnblogs.com/pengze0902/p/6562447.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
總結
以上是生活随笔為你收集整理的跨平台的.NET邮件协议MailKit组件解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ASP.NET Core] Middl
- 下一篇: .NET Core项目从xproj+pr