1. 程式人生 > >C#使用 MailKit獲取郵件中的附件(QQ郵箱/163網易郵箱)

C#使用 MailKit獲取郵件中的附件(QQ郵箱/163網易郵箱)

背景介紹:VS2017 | .net core | C# |  .netframwork 4.0

-----------------------------------------------------------------------

這東西本來沒打算記的,主要是連線郵件伺服器的時候百度搜出來的大多部落格不能用,浪費1小時,所以來記一下,主要看的是這個參考部落格,我在此基礎上做了改動

實現功能如下:

1. 通過授權碼連線到郵箱伺服器,並獲取到某資料夾下的郵件,以上出錯添加了能看到是哪塊的問題(對使用的郵箱需要你開啟STMP服務,設定規則以確保符合的郵件移動到此資料夾下)

2. 不開啟郵件,只根據主題進行篩選,同主題郵件取日期最新的那個(通過linq篩選實現)

3. 直接下載郵件中所有的普通附件(大於50M的是超大附件,只有連結,且無法通過程式碼下載(反正我不行,你行你試試))

4. 下載完成刪除同類舊壓縮包

因為頭擔心QQ強制改掉授權碼,要求換一個,我選了網易,這兩個都支援最大50M的,還有新浪什麼的,但沒註冊就沒用

關於授權碼:

網易:授權碼是你自己設定的,方便記憶 最多16位

QQ:授權碼給你分配的,12位,但明確說了可以有很多個不用記,但只要你改QQ密碼或什麼中心密碼,之前的全部失效

但連獲取郵箱資料夾內容這裡,QQ網易不一樣啊,網易真的坑,你愣愣的上來會獲不到“故意設定障礙,其實是網易需要我們表明我們登入的客戶端身份,具體的原理和命令參照我上一篇文章檢視IMAP ID COMMAND命令,下面只具體說說mailkit如何使用到這條命令,其實mailkit是提供這個命令的,足見mailkit的強大”   ---參考

這個部落格

一,nuget包,搜MailKit   找到適合你的版本,下載

-----------------------------------------------------------------------

二.  程式碼內容(QQ的)

            string account = "198416****@qq.com";
            string passWord = "jsknsh**********";//獲得的授權碼
            m_logger.LogInformation("開始到郵箱查詢更新包");

            //準備工作結束
            ImapClient client = new ImapClient();

            #region 連線到郵件伺服器
            try
            {
                //一、建立獲取郵件客戶端並連線到郵件伺服器。
                //帶埠號和協議的連線方式
                client.Connect("imap.qq.com", 993, true);
            }
            catch (ImapCommandException ex)
            {
                 m_logger.LogInformation($"---------嘗試連線時出錯:{0}------------" + ex.Message);
            }
            catch (ImapProtocolException ex)
            {
                 m_logger.LogInformation($"---------嘗試連線時的協議錯誤:{0}------------" + ex.Message);
            }
            catch (Exception ex)
            {
                 m_logger.LogInformation($"---------伺服器連線錯誤:{0}------------" + ex.Message);
            }

            try
            {
                // 二、驗證登入資訊,輸入賬號和密碼登入。
                client.Authenticate(account, passWord);
            }
            catch (AuthenticationException ex)
            {
                 m_logger.LogInformation($"---------無效的使用者名稱或密碼:{0}------------" + ex.Message);
            }
            catch (ImapCommandException ex)
            {
                 m_logger.LogInformation($"---------嘗試驗證錯誤:{0}------------" + ex.Message);
            }
            catch (ImapProtocolException ex)
            {
                 m_logger.LogInformation($"---------嘗試驗證時的協議錯誤:{0}------------" + ex.Message);
            }
            catch (Exception ex)
            {
                 m_logger.LogInformation($"---------賬戶認證錯誤:{0}------------" + ex.Message);
            }
            #endregion

            //  三、獲取郵箱資料夾。這次用到的的收件箱資料夾叫YiBaoRDZ。

            //獲取所有的資料夾
            //List<IMailFolder> mailFolderList = client.GetFolders(client.PersonalNamespaces[0]).ToList();
            //只獲取收件箱資料夾
            var folder = client.GetFolder("其他資料夾/YiBaoRDZ");

            //四、從資料夾獲取檔案
            //開啟資料夾並設定為讀的方式
            folder.Open(MailKit.FolderAccess.ReadOnly);

            //獲取大於2018-3-26時間的所有郵件的唯一Id
            var uidss = folder.Search(SearchQuery.DeliveredAfter(DateTime.Parse("2018-3-26")));

            //獲取郵件頭
            var MailHeaders = folder.Fetch(uidss, MessageSummaryItems.UniqueId | MessageSummaryItems.Full);
            //同主題郵件取日期最新
            var NeedMailIds = from a in MailHeaders
                       group a by a.NormalizedSubject into grp
                       let maxTime = grp.Max(a => a.Date.DateTime)
                       from row in grp
                       where row.Date.DateTime == maxTime
                       select row.UniqueId;

            if (NeedMailIds!=null)
            {
                //獲取郵件資訊
                foreach (var item in NeedMailIds)
                {
                    MimeMessage message = folder.GetMessage(item);
                    //提取該郵件所有普通附件
                    foreach (MimePart attachment in message.Attachments)
                    {
                        //下載附件
                        using (var cancel = new System.Threading.CancellationTokenSource())
                        {
                            string filePath = Path.Combine(outDirPath, attachment.FileName);
                            using (var stream = File.Create(filePath))
                            {
                                attachment.ContentObject.DecodeTo(stream, cancel.Token);
                            }
                        }
/////////////////////////////////從這裡開始是我自己寫的內容,和本次部落格無關//////////////////////////////////
                        // 雜湊值是否正確
                        string strHash;
                        string strLocalTmpFullPath = Path.Combine(direcotryDownload.FullName, attachment.FileName);
                        using (FileStream fs = new FileStream(strLocalTmpFullPath, FileMode.Open, FileAccess.Read))
                        {
                            strHash = Crypto.GetSHA1String(fs);
                        }
                        if (strHash != singleInfo.HashString)
                        {
                            m_logger.LogError("檔案雜湊值不匹配\n遠端雜湊值:" + singleInfo.HashString + "\n本地計算雜湊值:" + strHash);
                            continue;
                        }

                        //刪除同類舊版更新包
                        DirectoryInfo dyInfo = new DirectoryInfo(outDirPath);
                        //獲取資料夾下所有的檔案
                        foreach (FileInfo feInfo in dyInfo.GetFiles())
                        {
                            //判斷同名檔案日期是否小於今天,是則刪除
                            if (feInfo.CreationTime < DateTime.Today&& feInfo.Name.Contains(singleInfo.PackageName))
                                feInfo.Delete();
                        }
                    }


                }
            }
            
            //關閉資料夾
            folder.Close();

QQ官網幫助文件:QQ郵箱的POP3與SMTP伺服器是什麼  我用的SMTP,而SMTP需要身份驗證,密碼不再是郵箱的登陸密                                                                                                碼,而是授權碼    如何拿到授權碼

                              幫助中心

-----------------------------------------------------------------------

三.  程式碼內容(網易的)

//mailkit還提供查詢是否支援命令的語句,如果你不知道郵箱是否支援這個命令可以使用下面的函式來判斷:
        private static bool HasImapCapabilitiesId(ImapCapabilities sourceFlag, ImapCapabilities targetFlag)
        {
            return ((sourceFlag | targetFlag) == sourceFlag);
        }
            string account = "182379*****@163.com";
            string passWord = "zltdkj******";//獲得的授權碼
            m_logger.LogInformation("開始到郵箱查詢更新包");

            //Result result;
            List<PackageInfo> listPackages = m_managerPackages.GetAllExeInfos();//獲取可更新程式資訊(更新程式,主程式,校驗引擎,四個資料庫)
            List<DownloadedInfo> listDownloadeds = m_managerDownload.GetDownloadedPackageInfos();//獲得本地所有壓縮包檔案資訊

            DirectoryInfo direcotryDownload = m_managerDownload.GetDownloadDirectory();//獲得本地更新壓縮包檔案存放路徑
            if (null == direcotryDownload)
            {
                m_logger.LogError("獲取本地包存放目錄失敗");
                return;
            }

            #endregion
            //準備工作結束
            ImapClient client = new ImapClient();

            #region 連線到郵件伺服器
            try
            {
                //一、建立獲取郵件客戶端並連線到郵件伺服器。
                //帶埠號和協議的連線方式
                client.Connect("imap.163.com", 993, true);
            }
            catch (ImapCommandException ex)
            {
                 m_logger.LogInformation($"---------嘗試連線時出錯:{0}------------" + ex.Message);
            }
            catch (ImapProtocolException ex)
            {
                 m_logger.LogInformation($"---------嘗試連線時的協議錯誤:{0}------------" + ex.Message);
            }
            catch (Exception ex)
            {
                 m_logger.LogInformation($"---------伺服器連線錯誤:{0}------------" + ex.Message);
            }

            try
            {
                // 二、驗證登入資訊,輸入賬號和密碼登入。
                client.Authenticate(account, passWord);
            }
            catch (AuthenticationException ex)
            {
                 m_logger.LogInformation($"---------無效的使用者名稱或密碼:{0}------------" + ex.Message);
            }
            catch (ImapCommandException ex)
            {
                 m_logger.LogInformation($"---------嘗試驗證錯誤:{0}------------" + ex.Message);
            }
            catch (ImapProtocolException ex)
            {
                 m_logger.LogInformation($"---------嘗試驗證時的協議錯誤:{0}------------" + ex.Message);
            }
            catch (Exception ex)
            {
                 m_logger.LogInformation($"---------賬戶認證錯誤:{0}------------" + ex.Message);
            }
            #endregion

            //這是網易故意設定障礙,其實是網易需要我們表明我們登入的客戶端身份,具體的原理和命令參照我上一篇文章檢視IMAP ID COMMAND命令,下面只具體說說mailkit如何使用到這條命令,其實mailkit是提供這個命令的,足見mailkit的強大
            //判斷是否 新增ID COMMOND命令
            if (HasImapCapabilitiesId(client.Capabilities, ImapCapabilities.Id))
            {
                var clientImplementation = new ImapImplementation
                {
                    Name = "MeSince",
                    Version = "2.0"
                };
                var serverImplementation = client.Identify(clientImplementation);
            }

            //  三、獲取郵箱資料夾。這次用到的的收件箱資料夾叫YBrdz。

            //獲取所有的資料夾
            //List<IMailFolder> mailFolderList = client.GetFolders(client.PersonalNamespaces[0]).ToList();
            List<IMailFolder> mailFolderList = client.GetFolders(client.PersonalNamespaces[0]).ToList();
            IMailFolder InBoxFolder;
            if (mailFolderList != null)
            {
                InBoxFolder = mailFolderList.Find(o => o.FullName.ToUpper() == "YBRDZ");
                //InBoxFolder.Open(MailKit.FolderAccess.ReadOnly);
            }
            //只獲取收件箱資料夾
            var folder = client.GetFolder("YBrdz");

            //四、從資料夾獲取檔案
            //開啟資料夾並設定為讀的方式

            //folder.Open(MailKit.FolderAccess.ReadOnly);
            //已讀寫的方式開啟資料夾
            folder.Open(FolderAccess.ReadWrite);

            //獲取大於2018-3-26時間的所有郵件的唯一Id
            var uidss = folder.Search(SearchQuery.DeliveredAfter(DateTime.Parse("2018-3-26")));

            //獲取郵件頭
            var MailHeaders = folder.Fetch(uidss, MessageSummaryItems.UniqueId | MessageSummaryItems.Full);
            //同主題郵件取日期最新
            var NeedMailIds = from a in MailHeaders
                       group a by a.NormalizedSubject into grp
                       let maxTime = grp.Max(a => a.Date.DateTime)
                       from row in grp
                       where row.Date.DateTime == maxTime
                       select row.UniqueId;

            if (NeedMailIds!=null)
            {
                //獲取郵件資訊
                foreach (var item in NeedMailIds)
                {
                    MimeMessage message = folder.GetMessage(item);
                    //提取該郵件所有普通附件
                    foreach (MimePart attachment in message.Attachments)
                    {
                        //由附件名稱獲得資訊,進行比較
                        FileNameInfo singleInfo = FileNameInfo.Create(attachment.FileName);
                        // 是否已經下載過相同或更高版本
                        if (listDownloadeds.
                            Where(s => (s.Name == singleInfo.PackageName) && (s.Version >= singleInfo.Version)).
                            Count() > 0)
                        {
                            continue;
                        }
                        //下載附件
                        using (var cancel = new System.Threading.CancellationTokenSource())
                        {
                            string filePath = Path.Combine(outDirPath, attachment.FileName);
                            using (var stream = File.Create(filePath))
                            {
                                attachment.ContentObject.DecodeTo(stream, cancel.Token);
                                m_logger.LogInformation("更新壓縮包 " + attachment.FileName + " 已下載");
                            }
                        } 
/////////////////////////////////從這裡開始是我自己寫的內容,和本次部落格無關//////////////////////////////////

這次參考的部落格彙總: