使用MailKit發送郵件


.NET Core 使用MailKit發送電子郵件

Github:關於 MailKit

很多有經驗的.NET老程序員可能會說,發郵件有什么難的,十幾年前我們就能用.NET Framework自帶的SmtpClient發郵件了,並且.NET Core也能用。為啥還要寫這篇文章?

但是,萬物皆有始有終,最近我突然發現,SmtpClient 已經被微軟標記為棄用:

並且微軟官方欽點了一個繼任者:MailKithttps://github.com/jstedfast/MailKit

這是一個基於MimeKit的跨平台.NET郵件庫,支持IMAP、POP3、SMTP協議。它相比.NET自帶的SmtpClient,支持更廣泛的協議和更現代的電子郵件標准。因此微軟官方建議,SmtpClient只用來兼容老應用,如果開發新應用的話,直接使用MailKit。

並且,它是在MIT協議下開源的。意味着非常自由的使用,也可以由全世界的.NET開發者參與貢獻,一起維護和完善這個東西。

使用SMTP協議發送郵件

我得到這個好東西以后,第一步就是將使用SmtpClient的老代碼遷移到MailKit。因此,我的案例里只使用SMTP這一種協議來發郵件。

首先,使用NuGet安裝MailKit:

Visual Studio

Install-Package MailKit

.NET Core CLI

dotnet add package MailKit

構建 MimeMessage

MimeMessage是MailKit里代表一封電子郵件的對象,它和.NET自帶的MailMessage類型非常類似。比如添加主題和發件人:

var messageToSend = new MimeMessage { Sender = new MailboxAddress("發件人姓名", "發件人Email地址"), Subject = "主題", };

添加發件人信息和以前有所不同,MailKit居然支持多個發件人,所以From是一個集合類型,要通過Add方法來添加:

messageToSend.From.Add(new MailboxAddress("發件人姓名", "發件人郵箱賬號名"));

郵件正文(Body屬性)支持多種格式,最常用的是純文本和HTML。需要用TextPart類來安排,TextPart的構造函數里可以指定正文格式,例如HTML:

messageToSend.Body = new TextPart(TextFormat.Html) { Text = bodyText };

或者純文本

messageToSend.Body = new TextPart(TextFormat.Plain) { Text = bodyText };

添加收件人信息:

messageToSend.To.Add(new MailboxAddress("收件人Email地址"));

添加抄送(CC)信息:

messageToSend.Cc.Add(new MailboxAddress("抄送者Email地址"));

以下代碼演示了幾個步驟:

  1. 注冊郵件發送成功后的事件
  2. 連接服務器
  3. 驗證賬號
  4. 發送郵件
  5. 斷開連接
using (var smtp = new MailKit.Net.Smtp.SmtpClient())
{
    smtp.MessageSent += (sender, args) => { // args.Response };
    smtp.ServerCertificateValidationCallback = (s, c, h, e) => true;
    await smtp.ConnectAsync("smtp-mail.outlook.com", 587, SecureSocketOptions.StartTls);
    await smtp.AuthenticateAsync("賬號", "密碼");
    await smtp.SendAsync(messageToSend);
    await smtp.DisconnectAsync(true);
}

MessageSent事件里可以通過args參數,獲得服務器的響應信息,以便於記錄Log。

連接outlook.com的服務器需要設置為SecureSocketOptions.StartTls,不然會拒絕連接。對於其他服務器,可以試試 SecureSocketOptions.Auto

參考:.NET Core 使用MailKit發送電子郵件

SmtpClient與MailKit對比

MailKit文檔中的一些翻譯

文檔:http://www.mimekit.net/docs/html/Introduction.htm

1、MessageFlags:消息標志的枚舉

     
  None 0
  Seen 1 消息標記為 已讀
  Answered 2 該消息已得到答復
  Flagged 4 該消息已標記為重要
  Deleted 8 刪除
  Draft 16 草稿
  Recent 32 該消息剛到達文件夾中。
  UserDefined 64 文件夾允許使用用戶定義的標志。

 

2、MessageSummaryItems

  • Envelope :消息信封,其中包含消息的簡短摘要。其中包含To、From、Date、Subject...
  • Body
  • Flags:MessageFlags
  • UniqueID
  • EmailID
  • Full

 

IMAP接收郵件

接收郵件協議有pop3、Imap,比POP3支持更重要的是IMAP支持。 這是一個從IMAP服務器檢索消息的簡單用例:

using System;

using MailKit.Net.Imap;
using MailKit.Search;
using MailKit;
using MimeKit;

namespace TestClient {
    class Program
    {
        public static void Main (string[] args)
        {
            using (var client = new ImapClient ()) {
                // For demo-purposes, accept all SSL certificates
                client.ServerCertificateValidationCallback = (s,c,h,e) => true;

                client.Connect ("imap.friends.com", 993, true);

                client.Authenticate ("joey", "password");

                // The Inbox folder is always available on all IMAP servers...
                var inbox = client.Inbox;
                inbox.Open (FolderAccess.ReadOnly);

                Console.WriteLine ("Total messages: {0}", inbox.Count);
                Console.WriteLine ("Recent messages: {0}", inbox.Recent);

                for (int i = 0; i < inbox.Count; i++) {
                    var message = inbox.GetMessage (i);
                    Console.WriteLine ("Subject: {0}", message.Subject);
                }

                client.Disconnect (true);
            }
        }
    }
}
View Code

但是,您可能想對IMAP做更復雜的事情,例如獲取摘要信息(summary),以便您可以在郵件客戶端中顯示郵件列表,而不必先從服務器下載所有郵件

//為兩個索引(包括兩個索引)之間的消息獲取消息摘要
            foreach (var summary in inbox.Fetch(0,-1,MessageSummaryItems.Full | MessageSummaryItems.UniqueId))
            {
                Console.WriteLine("[summary] {0:D2}: {1}", summary.Index, summary.Envelope.Subject);
            }
MessageSummaryItems 是MailKit.MessageSummary字段的位字段,每個枚舉值是想要獲取的屬性。

通過調用Fetch() 是填充MessageSummary的哪些屬性

郵件協議之IMAP指令講解

Fetch命令的結果還可以用於下載單個MIME部分,而不是下載整個消息。 例如:

 private static void GetMime(ImapClient client)
        {
            var inbox = client.Inbox;

            //下載MIME協議格式中的某個域,而不是下載整個郵件內容
            foreach (var summary in inbox.Fetch(0, -1, MessageSummaryItems.UniqueId | MessageSummaryItems.BodyStructure))
            {
                if (summary.TextBody != null)
                {
                    // this will download *just* the text/plain part
                    var text = inbox.GetBodyPart(summary.UniqueId, summary.TextBody);
                }

                if (summary.HtmlBody != null)
                {
                    // this will download *just* the text/html part
                    var html = inbox.GetBodyPart(summary.UniqueId, summary.HtmlBody);
                }

                // 【獲取圖片附件】if you'd rather grab, say, an image attachment... it might look something like this:
                if (summary.Body is BodyPartMultipart)
                {
                    var multipart = (BodyPartMultipart)summary.Body;

                    var attachment = multipart.BodyParts.OfType<BodyPartBasic>().FirstOrDefault(x => x.FileName == "logo.jpg");
                    if (attachment != null)
                    {
                        // this will download *just* the attachment
                        var part = inbox.GetBodyPart(summary.UniqueId, attachment);
                    }
                }
            }
}
View Code

還可以做排序和搜索:inbox.Search、 inbox.Sort

當然,除了下載消息外,您還可以獲取匹配消息的摘要信息Summary,或者使用返回的UID進行任何其他操作。

如何瀏覽文件夾? MailKit也可以這樣做:

// Get the first personal namespace and list the toplevel folders under it.
var personal = client.GetFolder (client.PersonalNamespaces[0]);
foreach (var folder in personal.GetSubfolders (false))
    Console.WriteLine ("[folder] {0}", folder.Name);

也可以:

List<IMailFolder> mailFolders = client.GetFolders(client.PersonalNamespaces[0]).ToList();
            mailFolders.ForEach(q => Console.WriteLine(q.FullName));

如果IMAP服務器支持SPECIAL-USE或XLIST(GMail)擴展名,則可以使用以下預定義的“全部”,“草稿”,“已標記”(又名“重要”),Junk“垃圾郵件”,“已發送”,“垃圾箱”等文件夾:

if ((client.Capabilities & (ImapCapabilities.SpecialUse | ImapCapabilities.XList)) != 0) {
    var drafts = client.GetFolder (SpecialFolder.Drafts);
} else {
    // maybe check the user's preferences for the Drafts folder?
}

如果IMAP服務器不支持SPECIAL-USE或XLIST擴展名,則必須提出自己的啟發式方法來獲取“已發送”,“草稿”,“廢紙rash”等文件夾。 例如,您可能使用類似以下的邏輯:

static string[] CommonSentFolderNames = { "Sent Items", "Sent Mail", "Sent Messages", /* maybe add some translated names */ };

static IFolder GetSentFolder (ImapClient client, CancellationToken cancellationToken)
{
    var personal = client.GetFolder (client.PersonalNamespaces[0]);
    
    return personal.GetSubfolders (false, cancellationToken).FirstOrDefault (x => CommonSentFolderNames.Contains (x.Name));
}

另一個選項可能是允許您的應用程序用戶配置他或她要用作其“已發送”文件夾,“草稿”文件夾,“廢紙folder”文件夾等的文件夾。

如何處理這取決於您。

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

參考:創建基於MailKit和MimeKit的.NET基礎郵件服務

 

問題

在采用IMAP收取163郵件時,報錯登錄不安全

報錯內容:The IMAP server replied to the 'EXAMINE' command with a 'NO' response: EXAMINE Unsafe Login. Please contact kefu@188.com for help

(IMAP服務器使用“否”響應回復了“ EXAMINE”命令:EXAMINE不安全登錄。 請聯系kefu@188.com尋求幫助)

解決方法:

對163郵箱設置: 收郵件NO Select Unsafe Login. Please contact kefu解決辦法

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM