跨平台的.NET邮件协议MailKit组件解析
發(fā)起的.NET Core開源組織號(hào)召,進(jìn)展的速度是我自己也沒有想到的,很多園友都積極參與(雖然有些人誠心砸場(chǎng)子,要是以我以前的寶脾氣,這會(huì)應(yīng)該被我打住院了吧,不過幸好是少數(shù),做一件事總有人說好,也有人說是用武漢話說“鬧眼子”),.NET社區(qū)不是沒有樂于共享知識(shí)的人,只是沒有一個(gè)完整和良好的生態(tài)環(huán)境,總之希望國內(nèi)的.NET發(fā)展越來越強(qiáng)大。我在這里想到一句話“我們希望自己可以做巨浪,但我們也甘愿做巨浪來襲前的小浪”。
? ?上面扯淡完畢(我這人干正事前,都要將一些扯淡的話,這個(gè)習(xí)慣改不掉了...)
? ?項(xiàng)目中為了及時(shí)的通信,有直接發(fā)數(shù)據(jù)到頁面,也有利用短信通知,也有我門今天介紹的郵件組件。我們今天的主要任務(wù)就是講解一下有一個(gè).NET的免費(fèi)開源的郵件組件MailKit。本文將一如既往的結(jié)合實(shí)例和組件底層代碼講解一下相關(guān)組件的知識(shí)。(項(xiàng)目招人的時(shí)候,我都會(huì)問一下.NET的底層原理,有一個(gè)大神問我這樣有什么意義嗎?我們也寫不出.NET底層那樣的優(yōu)秀處理方式,為何取了解這些,其實(shí)我個(gè)人覺得,問底層的原理,只是向?yàn)榱烁玫奶幚硪恍┏绦虺霈F(xiàn)的問題,以及對(duì)程序編碼的時(shí)候,選擇最合適的方式提升性能,任何一種方式都有優(yōu)勢(shì)和劣勢(shì),.NET的類庫代碼也是如此,如果我們知道.NET的底層實(shí)現(xiàn),我們?cè)陧?xiàng)目的需求實(shí)現(xiàn)時(shí),可以根據(jù).NET底層實(shí)現(xiàn),選擇合適的方式,以求性能最優(yōu))。
一.Mailkit組件概述
? ?項(xiàng)目中使用Email的操作機(jī)會(huì)比較多,一般稍微大一點(diǎn)的項(xiàng)目,都會(huì)使用到郵件操作這一個(gè)操作。對(duì)于.NET郵件操作的組件和方式比較多,今天我們就介紹一款郵件操作的組件MailKit,這個(gè)郵件組件是一個(gè)開源免費(fèi)的,我們現(xiàn)在就來了解一下這一個(gè)組件的特點(diǎn)。MimeKit旨在通過盡可能接近地遵循MIME規(guī)范來解決這個(gè)問題,同時(shí)還為程序員提供了一個(gè)非常容易使用的高級(jí)API。
? ?組件的支持的客戶端類型比較多,例如SMTP客戶端、POP3客戶端、IMAP客戶端。該組件是一個(gè)跨平臺(tái)的Email組件,該組件支持.NET 4.0,.NET 4.5,Xamarin.Android,Xamarin.iOS,Windows Phone 8.1等等平臺(tái)。該組件提供了一個(gè)MIME解析器,組件具備的解析特性靈活、性能高、很好的處理各種各樣的破碎的MIME格式化。MimeKit的性能實(shí)際上與GMime相當(dāng)。
? ?該組件在安全性的還是比較高的,處理安全的方式較多,SASL認(rèn)證、支持S / MIME v3.2、支持OpenPGP、支持DKIM簽名等等方式。Mailkit組件可以通過CancellationToken取消對(duì)應(yīng)的操作,CancellationToken傳播應(yīng)取消操作的通知,一個(gè)的CancellationToken使線程,線程池工作項(xiàng)目之間,或取消合作任務(wù)的對(duì)象。過實(shí)例化CancellationTokenSource對(duì)象來創(chuàng)建取消令牌,該對(duì)象管理從其CancellationTokenSource.Token屬性檢索的取消令牌。然后,將取消令牌傳遞到應(yīng)該收到取消通知的任意數(shù)量的線程,任務(wù)或操作。令牌不能用于啟動(dòng)取消。
??MailKit組件支持異步操作,在內(nèi)部編寫的有關(guān)I/O異步操作的類。
二.MailKit實(shí)例:
? ? 上面介紹了MailKit組件的背景和特點(diǎn),這里就介紹一下Email組件的簡(jiǎn)單應(yīng)用。
? 1.創(chuàng)建郵件方式:
public void SentEmail(string path)
? ? ? ? {
? ? ? ? ? ? var message = new MimeMessage();
? ? ? ? ? ? //獲取From標(biāo)頭中的地址列表,添加指定的地址
? ? ? ? ? ? message.From.Add(new MailboxAddress("Joey", "joey@friends.com"));
? ? ? ? ? ? //獲取To頭中的地址列表,添加指定的地址
? ? ? ? ? ? message.To.Add(new MailboxAddress("Alice", "alice@wonderland.com"));
? ? ? ? ? ? //獲取或設(shè)置消息的主題
? ? ? ? ? ? message.Subject = "How you doin?";
? ? ? ? ? ? // 創(chuàng)建我們的消息文本,就像以前一樣(除了不設(shè)置為message.Body)
? ? ? ? ? ? var body = new TextPart("plain")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Text = @"Hey Alice-- Joey"
? ? ? ? ? ? };
? ? ? ? ? ? // 為位于路徑的文件創(chuàng)建圖像附件
? ? ? ? ? ? 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)
? ? ? ? ? ? };
? ? ? ? ? ? // 現(xiàn)在創(chuàng)建multipart / mixed容器來保存消息文本和圖像附件
? ? ? ? ? ? var multipart = new Multipart("mixed")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? body, attachment
? ? ? ? ? ? };
? ? ? ? ? ? // 現(xiàn)在將multipart / mixed設(shè)置為消息正文?
? ? ? ? ? ? message.Body = multipart;
? ? ? ? }
? ?調(diào)用該組件發(fā)送郵件和為郵件添加附件是比較簡(jiǎn)單的,第一步是實(shí)例化MimeMessage對(duì)象,對(duì)于該對(duì)象的解析將在下面進(jìn)行,得到MimeMessage對(duì)象后,指定郵件的地址和主題等等相關(guān)信息。第二步實(shí)例化TextPart對(duì)象,為對(duì)象設(shè)定文本信息。若需要問郵件創(chuàng)建文件的附件,可以使用MimePart對(duì)象,包含內(nèi)容(如消息正文文本或)的葉節(jié)點(diǎn)MIME部分一個(gè)附件。第四步為創(chuàng)建的郵件主體和文本以及附件信息后,可以創(chuàng)建Multipart對(duì)象,創(chuàng)建郵件容器,用來裝載文本信息和附件。最后調(diào)用MimeMessage.body屬性獲取或設(shè)置消息的正文。
? ? 2.郵件信息的解析:
var message = MimeMessage.Load(stream);? ?郵件的信息我們需要進(jìn)行對(duì)應(yīng)的解析,這里我們使用MimeMessage的Load方法,該方法從指定的流加載MimeKit.MimeMessage。另一個(gè)加載數(shù)據(jù)的方式,可以使用MimeParser類,這里就不再解析了。
? ? 3.郵件的接收:
public static void HandleMimeEntity(MimeEntity entity)
? ? ? ? {
? ? ? ? ? ? //MimeEntity轉(zhuǎn)化為Multipart實(shí)體
? ? ? ? ? ? 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;
? ? ? ? }
? ?以上是對(duì)接收到的消息的一個(gè)遍歷,采用遞歸遍歷MIME結(jié)構(gòu)。MIME是內(nèi)容的樹結(jié)構(gòu),很像一個(gè)文件系統(tǒng)。MIME確實(shí)定義了一組通用規(guī)則,用于郵件客戶端如何解釋MIME部分的樹結(jié)構(gòu)。的?內(nèi)容處置頭是為了給接收客戶端提供提示以哪些部分是為了顯示作為消息體的一部分,并且意在被解釋為附件。另外兩種方式這離就不做介紹了。
三.MailKit核心對(duì)象解析
? ?上面介紹了Email的基本操作就不做過多的介紹,在使用該組件時(shí),較為的簡(jiǎn)單。這里就來看看該組件的類型結(jié)構(gòu)和一些核心對(duì)象。類庫結(jié)構(gòu)有如下圖:
? ? 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個(gè)方法重載。該方法返回一個(gè)MimeMessage對(duì)象,有源碼可以看出,在該方法內(nèi)部創(chuàng)建了一個(gè)MimeParser對(duì)象,MimeParser包含內(nèi)容(例如郵件正文文本或附件)的葉節(jié)點(diǎn)MIME部分。調(diào)用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);
? ? ? ? ? ? }
? ? ? ? }
? ? 該屬性獲取解碼的文本內(nèi)容。該屬性是一個(gè)可讀可寫的屬性。ContentType.Parameters["charset"]用于獲取charset參數(shù)的值。該方法用來將參數(shù)的值設(shè)置為數(shù)據(jù)流并設(shè)置對(duì)應(yīng)的編碼??吹竭@里的異常處理結(jié)構(gòu),就想簡(jiǎn)單的談幾句,.NET的異常比較的薄弱,很多時(shí)候在寫.NET的異常時(shí)就更加的簡(jiǎn)單,以上是對(duì)異常知識(shí)捕獲,有些地方并沒有做處理,有些地方是對(duì)異常的地方進(jìn)行恢復(fù)。
? ?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寫入到指定的數(shù)據(jù)流中,該方法接受參數(shù)options格式選項(xiàng)。stream輸出數(shù)據(jù)流,contentOnly判斷是否可寫。該方法定義為虛方法,在繼承此方法后,可以在子類種對(duì)該方法進(jìn)行重寫。
四.總結(jié)
? ?本人覺得在項(xiàng)目開發(fā)中,如果引入了第三方組件,我們盡量引入組件的源碼,這樣我們對(duì)整個(gè)組件的結(jié)構(gòu)有一個(gè)認(rèn)識(shí),組件的實(shí)現(xiàn)方式我們也可以進(jìn)行細(xì)致了解,尤其是我們?cè)谶M(jìn)行調(diào)試的事后更加有用,我們可以逐一的進(jìn)行斷點(diǎn)調(diào)試。以上是對(duì)該組件的一個(gè)簡(jiǎn)單介紹,有興趣的可以去深入的了解和學(xué)習(xí)。
原文地址:http://www.cnblogs.com/pengze0902/p/6562447.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的跨平台的.NET邮件协议MailKit组件解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [ASP.NET Core] Middl
- 下一篇: 老司机实战Windows Server