1. 程式人生 > >建立基於MailKit和MimeKit的.NET基礎郵件服務

建立基於MailKit和MimeKit的.NET基礎郵件服務

      郵件服務是一般的系統都會擁有和需要的功能,但是對於.NET專案來說,郵件服務的建立和使用會較為的麻煩。.NET對於郵件功能提供了System.Net.Mail用於建立郵件服務,該基礎服務提供郵件的基礎操作,並且使用也較為的簡單。對於真正將該功能使用於專案的人,就會慢慢發現其中的優缺點,甚至有些時候不能忍受其中的問題。在這裡介紹一種微軟用於替代System.Net.Mail的郵件服務元件MailKit和MimeKit,官網地址:http://www.mimekit.net/。GitHub地址:https://github.com/jstedfast/MimeKit。下面就具體的介紹一下。

  一.MailKit和MimeKit基礎概述:

       MailKit元件的支援的客戶端型別比較多,例如SMTP客戶端、POP3客戶端、IMAP4客戶端。該元件是一個跨平臺的Email元件,該元件支援.NET 4.0,.NET 4.5,Xamarin.Android,Xamarin.iOS,Windows Phone 8.1等等平臺。

     640?wx_fmt=png&wxfrom=5&wx_lazy=1

      MimeKit提供了一個MIME解析器,元件具備的解析特性靈活、效能高、很好的處理各種各樣的破碎的MIME格式化。MimeKit的效能實際上與GMime相當。

      該元件在安全性的還是比較高的,處理安全的方式較多,SASL認證、支援S / MIME v3.2、支援OpenPGP、支援DKIM簽名等等方式。Mailkit元件可以通過CancellationToken取消對應的操作,CancellationToken傳播應取消操作的通知,一個的CancellationToken使執行緒,執行緒池工作專案之間,或取消合作任務的物件。過例項化CancellationTokenSource物件來建立取消令牌,該物件管理從其CancellationTokenSource.Token屬性檢索的取消令牌。然後,將取消令牌傳遞到應該收到取消通知的任意數量的執行緒,任務或操作。令牌不能用於啟動取消。

     MailKit元件支援非同步操作,在內部編寫的有關I/O非同步操作的類。

  二.建立基礎郵件服務:

           介紹過MailKit和MimeKit組建的基礎資訊,接下來就介紹一下如何使用兩個元件的基本功能,在這裡我將基本操作做了一個簡單的封裝,一般的專案可以直接引用封裝好的類,大家可以根據實際的情況對該元件進行擴充套件。

          1.郵件傳送基礎服務API

/// <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;

        }

    }

   2.組裝郵件訊息:

/// <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;

        }

    }

   3.郵件基礎服務實體:

/// <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類提供的功能很豐富,例如連線郵件伺服器,郵件賬戶認證,組裝郵件訊息,獲取郵件伺服器配置資訊等等方法的提供,可以讓我們在專案中快速的獲取郵件服務的所有資訊。

           使用過郵件功能的專案 都會有困擾,客戶端與郵件伺服器的連線是否成功,以及郵件是否傳送成功狀態沒有辦法很快的獲取,只能根據郵件伺服器返回的一場狀態進行判斷。但是MailKit提供對應的方法和異常類,對郵件伺服器返回的異常資訊進行解析,客戶端可以根據這些異常類獲取郵件狀態。

           MailKit元件的提供了ProtocolLogger類,該類用於記錄SMTP操作基礎資訊,該類作用為記錄郵件服務日誌。在郵件傳送完畢後,需要及時的關閉連線,呼叫Disconnect(true)方法。

原文:https://www.cnblogs.com/pengze0902/p/8519715.html

.NET社群新聞,深度好文,歡迎訪問公眾號文章彙總 http://www.csharpkit.com

640?wx_fmt=jpeg