工作上有從Gmail讀信取出附檔的需求,先前在點部落上看過不少用Gmail SMTP送信的範例(by 艾小克dotjum),但一直沒看到讀信範例,在網路爬文也只陸續看到一些片斷,經過一番嘗試,總算拼湊出完整的收信並移至垃圾桶程式範例,特此分享。

關於收信功能,Gmail提供了POP3及IMAP兩種收信協定[補充],POP3較簡陋,只提供讓你將信全部抓回家的基本功能;而IMAP則還能支援讀取不同信件匣、設定"已閱讀"、移動信件到其他資料夾... 等功能,操作結果從Gmail網頁介面也看得到,已有幾分Gmail API的味道。因此,就決定使用較先進的IMAP協定與Gmail溝通。

目前已找得到不少使用.NET實作IMAP協定的元件或程式庫(免費跟收錢的都有),但由於Gmail IMAP一律要走SSL,並不是全部元件都支援的。評估之後,決定使用CodePlex上的Open Source專案--MailSystem.NET

程式的主流程為: 開啟SSL連線,逐一讀取收信匣中的信件,將信件內文HTML及附檔逐一存檔後,再將信件移至垃圾桶。

程式碼如下,補充說明我寫在註解裡,請參考:

        static void Main(string[] args)
        {
            Imap4Client clnt = new Imap4Client();
            //使用ConnectSsl進行加密連線
            var hmm = clnt.ConnectSsl("imap.gmail.com", 993);
            //登入
            clnt.Login("blahblah@gmail.com", "blahblah");
            //取得收件匣
            Mailbox inbox = clnt.SelectMailbox("inbox");
            //因讀完信就會移至垃圾桶,故由後讀到前,以免序號變動
            for (int n = inbox.MessageCount; n > 0; n--)
            {
                //取回第n封信
                ActiveUp.Net.Mail.Message m = inbox.Fetch.MessageObject(n);
                //為每封郵件建立專屬資料夾(要換掉主旨不能當資料夾名稱的字元)
                string msgFolder = string.Format("{0:yyyyMMddHHmmsss}-{1}", 
                    m.ReceivedDate, ReplaceInvalidPathChars(m.Subject));
                if (!Directory.Exists(msgFolder))
                    Directory.CreateDirectory(msgFolder);
                //將信件內文(HTML)寫入MailBody檔案
                string f = Path.Combine(msgFolder, "MailBody.html");
                File.WriteAllText(f, m.BodyHtml.Text);
                //逐一寫入附件檔案
                foreach (MimePart att in m.Attachments)
                {
                    f = Path.Combine(msgFolder,
                        //換掉不能當檔案名的字元
                        ReplaceInvalidFileNameChars(att.Filename));
                    File.WriteAllBytes(f, att.BinaryContent);
                }
                //將信件移至垃圾桶(CopyMessage即可產生移動資料夾的效果)
                inbox.CopyMessage(n, "[Gmail]/Trash");
                Console.WriteLine("{0}.{1}", n, m.Subject);
            }
            Console.Read();
        }
        //將不可做為路徑名稱的字元換成_
        static string ReplaceInvalidPathChars(string raw)
        {
            foreach (char c in Path.GetInvalidPathChars())
                raw = raw.Replace(c, '_');
            return raw;
        }
        //將不可做為檔案名稱的字元換成_
        static string ReplaceInvalidFileNameChars(string raw)
        {
            foreach (char c in Path.GetInvalidFileNameChars())
                raw = raw.Replace(c, '_');
            return raw;
        }


Comments

# by 鑽石

代碼完全看不懂!

# by CKY

在mailboxes的名字的部分,是否可以使用中文呢? 還是有需要透過某種轉編碼的動作才能正常使用呢

# by CKY

回覆自己的問題 使用IMAP取得的郵件夾名稱需要進行解碼的動作 要更改郵件資料夾名稱時也需要做編碼的動作 可搜尋"IMAP郵件夾名稱編碼和解碼"

# by Fraid

不能執行耶 是否有exmple 可提供一下

# by Fraid

你好: 有許多類別都找不到,使得執行失敗,是否能是供範例供參考 萬分感謝

# by Jeffrey

to Fraid, 文中的範例程式需要參考由CodePlex下載MailSystem.NET專案,Imap4Client等類別就是MailSystem.NET提供的。

# by Fraid

想再請問大大 不知是否這個檔 MailSystem.NET.December.2009 : http://mailsystem.codeplex.com/releases/view/37256 還有我想知你-加入參考-的 NET(DLL)檔是哪幾個, 程式碼using有哪些?

# by Fraid

將MailSystem.NET.December.2009.zip解壓後 獲得Release,裡面有很多檔 不知如何使用

# by Jeffrey

to Fraid, 請在專案加入參考\Release\Samples\Lib\ActiveUp.Net.Common.dll 及 ActiveUp.Net.Imap4.dll, 並using ActiveUp.Net.Mail; using System.IO; 應詃就可以順利編譯。

# by Fraid

在 inbox.CopyMessage(n, "[Gmail]/Trash");------(1) 中發生錯誤,將這//掉後就可以讀取信件, 有幾個問題想請問你一下, 1、在(1)code中,為何會發生錯誤,是否需在gmail標籤中設定,目前我都設顯示。 2、gmail讀取信件都這麼慢嗎?(因為我也嘗試過其他以pop發怖的dll,也是很慢)。 3、不知大大所說的 [而IMAP則還能支援讀取不同信件匣]-讀取不同信件匣,是指什麻意思 [設定"已閱讀]-不知code哪裡修改 是否已閱讀 [移動信件到其他資料夾]-目前程式移至垃圾桶發生錯誤,不知如何解決,假如解決 那如何將mail指定移到像草稿或其它地方。

# by Fraid

還有請問一下 是否有MailSystem.NET的使用說明文件? 不然好難去找尋到須使用的類別。 謝謝

# by Jeffrey

to Fraid, 1. 我跑了測試是成功的,不過我的Gmail介面語系設定為English(US),猜想它會影響資料匣名稱,設定成其他語言後可能就不叫"[Gmail]/Trash"了,例如: 中文叫"垃圾桶",不過依之前的經驗,遇到中文時有時會因編碼因素而失敗,有可能要改元件的Source Code解決。(將介面設成英文避開此問題也是一解) 2.依我的實測,的確不快。 3.clnt.SelectMailbox("inbox") <== 這就是選取了"inbox"信件匣,傳入不同參數就可以選取不同信件匣。而Gmail處理已閱讀的做法,我倒沒有研究出來。至於移動資料匣失敗的問題,請參考第1點。 4.Open Source的程式庫在文件及範例上不若商業元件那麼完整,使用時可能要多靠自己一點,Codeplex上也有討論區,不過不保證一定可以獲得完善的回應與支援,有時甚至要自己去追Source Code追查或解決問題。是種負擔也是種樂趣啦~~

# by Fraid

不過我覺得感覺pop快和好用 不知大大有用過pop嗎 目前我send mail、get list都可以, 差在download att,不知你是否了解這一塊通訊 謝謝

# by Fraid

謝謝大大的幫助 我已對這領域通訊方面有所了解了,功能也寫出來了 非常感謝

# by Jeffrey

to Fraid, 恭喜!! 有心得也記得跟大家分享哦~

# by Ricky

請問大大 小弟依照您的說明在參考加入 ConsoleApplication1\ConsoleApplication1\Release\Samples\Lib\ActiveUp.Net.Common.dll 和 ConsoleApplication1\ConsoleApplication1\Release\Samples\Lib\ActiveUp.Net.ActiveUp.Net.Imap4.dll 並在命名空間 using ActiveUp.Net.Mail; 可是VS出現以下 警告 無法解析所參考的組件 "ActiveUp.Net.Common", 因為它在 "System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 上有相依性, 而後者不在目前的目標 Framework ".NETFramework,Version=v4.0,Profile=Client" 中。 請移除不在目標 Framework 中的組件參考, 或考慮重新設定專案的目標。 請問出了什麼問題??

# by Jeffrey

to Ricky, 你應該是選到了.NET Framework 4 Client Profile,請改選.NET Framework 4。詳情可參考小朱的文章: http://www.dotblogs.com.tw/regionbbs/archive/2010/03/25/vs2010.net.4.client.profile.aspx

# by Ricky

大大你好 小弟的問題解決了 在發問之前小弟就GOOGLE過這個解決方法 但解決不了 原來是要在 加入參考之前就更改到.NET Framework 4

# by Ricky

忘了謝謝大大^^

# by Ricky

報告大大,小弟發現 m.ReceivedDate 這個類別 的時間不對耶 HHmm 現在台灣時間是1043 可是 FORMAT 輸出的結果 是 0243 請問大大怎麼辦

# by Jeffrey

to Ricky, 差了8小時,應是時區問題。ReceiveDate裡用的是UTC標準時間,要變成台灣本地時間需經過轉換,我想改成m.ReceiveDate.ToLocalTime()應該就會符合你的要求了。

# by Ricky

To,Jeffrey大 m.ReceiveDate.ToLocalTime() 有符合小弟的需求了 謝謝 大大 以上回覆的資訊也是在 Codeplex上 查的嗎?

# by Jeffrey

to Ricky, 不客氣。ReceiveDate的時區問題倒不是從CodePlex查的,是由差8小時這件事去推敲的,屬於老人家直覺反射的一部分,呵~

# by 張小呆

黑暗大您好: 近日參考您的範例在專案上開發相關 Mail 的功能 可是每當要取得相關 Mail 清單時 抓取的時間總是很久 MAIL 的數量也只是 124 封而已 不曉得 黑暗大 您在測試時是否也有相同的問題存在嘛!? 或者您是否有改善的做法呢!?

# by KTCHEN

回應樓上 可以用此判斷是否已讀 if (inbox.Fetch.Flags(n).Merged == "()") {} 雖然還是一樣慢.....

# by ChiChi

To Fraid 垃圾桶應該是inbox.CopyMessage(n, "[Gmail]/Trash");->inbox.CopyMessage(n, "[Gmail]/Bin");

Post a comment