基于Lumisoft.NET组件,使用IMAP协议收取邮件
在早期一直使用Lumisoft.NET组件来进行邮件的处理查找,对于邮件的处理非常方便,之前在随笔《基于Lumisoft.NET组件的POP3邮件接收和删除操作》中也介绍过基于POP3和SMPT进行邮件的收发处理,一般邮件服务器对于Pop3都是支持很好的,常规使用测试多个服务器都没问题,所以就没怎么研究IMAP协议的处理,本篇随笔基于原来POP3 的需求扩展了IMAP协议的处理。
1、创建IMAP收件辅助类
为了方便进行收取IMAP邮件的处理,我们创建一个ImapHelper,并传入相关用到的一些参数,用来封装收件的处理操作。
如下辅助类所示,传入服务器域名地址,端口,是否SSL,用户名和密码等信息。
接下来,我们需要连接服务器,并尝试获取授权信息,如果通过,则可以进行下一步获取邮件信息的操作,如下代码所示。
/// <summary> /// 收取邮件操作 /// </summary> public void Receive() { using (var client = new IMAP_Client()) { //创建日志处理 client.Logger = new Logger(); client.Logger.WriteLog += new EventHandler<WriteLogEventArgs>(WriteLog);//响应记录显示 //使用账号密码,连接服务器 client.Connect(server, port, useSsl); //登录获取授权操作 client.Login(username, password); //var identity = client.AuthenticatedUserIdentity; //获取各个邮箱目录的概要信息 client.GetFolders(null).ToList().ForEach(f => { Console.WriteLine(f.FolderName); var s = client.FolderStatus(f.FolderName); s.ToList().ForEach(sIt => { Console.WriteLine("总数:{0},未读:{1},最近{2}", sIt.MessagesCount, sIt.MessagesCount, sIt.UnseenCount); }); });
我们登录获得授权后,测试获取各个目录的概要邮件信息,如总邮件数量,以及未读数量等等。
然后通过选择具体的邮箱目录,并设置返回信息包含的内容格式,以及从服务器返回那些序号的邮件等等,如下代码所示。
//选择邮箱 client.SelectFolder("INBOX"); //首先确定取第x到第n封邮件,"1:*"表示第1封到最后一封 var seqSet = IMAP_t_SeqSet.Parse("1:*"); var items = new IMAP_t_Fetch_i[] { new IMAP_t_Fetch_i_Envelope(), //邮件的标题、正文等信息 new IMAP_t_Fetch_i_Uid(), //返回邮件的UID号,UID号是唯一标识邮件的一个号码 new IMAP_t_Fetch_i_Flags(), //此邮件的标志,应该是已读未读标志 new IMAP_t_Fetch_i_InternalDate(),//貌似是收到的日期 new IMAP_t_Fetch_i_Rfc822() //Rfc822是标准的邮件数据流,可以通过Lumisoft.Net.Mail.Mail_Message对象解析出邮件的所有信息 };
接着我们通过传入条件,并给他一个回调匿名函数处理相关的邮件信息,如下所示。
//Fetch 第一个参数false时seqSet有效 client.Fetch(false, seqSet, items, (s, e) => { //处理邮件的匿名函数内容 });
接着我们处理邮件信息的转换,吧邮件信息转换为Mail_Message对象的信息,这个包含邮件相关的头部信息,正文,以及附件信息等全部内容。
var email = e.Value as IMAP_r_u_Fetch; if (email.Rfc822 != null) { email.Rfc822.Stream.Position = 0; var mime_message = Mail_Message.ParseFromStream(email.Rfc822.Stream); email.Rfc822.Stream.Close();
然后我们把邮件的信息进一步转换为我们需要存储在数据库的对象信息,最后写入数据库即可。
receiveInfo.ReceivedDate = DateTime.Now;//接收本地时间 receiveInfo.Company_ID = this.companyId; receiveInfo.User_ID = this.userId; receiveInfo.Email = this.email;//接收Email账号 receiveInfo.MailConfig_ID = this.mailConfig_ID;//接收Email账号的配置记录ID //每封Email会有一个在Pop3服务器范围内唯一的Id,检查这个Id是否存在就可以知道以前有没有接收过这封邮件 receiveInfo.MailUid = email.UID.UID.ToString(); try { //可能会出现【LumiSoft.Net.ParseException: Header field 'Date' parsing failed】异常错误。 receiveInfo.SendDate = mime_message.Date; } catch (Exception ex) { receiveInfo.SendDate = Convert.ToDateTime("1900-1-1");//错误赋值一个日期 error = string.Format("转换邮件的Date出错:账号{0} 邮件标题:{1}", username, mime_message.Subject); LogTextHelper.Error(error, ex); } //可能出现乱码问题,通过函数进行转换 receiveInfo.Title = mime_message.Subject;//DecodeString(mime_header.Subject); receiveInfo.MailBody = mime_message.BodyText; try { if (!string.IsNullOrEmpty(mime_message.BodyHtmlText)) { receiveInfo.MailBody = mime_message.BodyHtmlText; } } catch { //屏蔽编码出现错误的问题,错误在BodyText存在而BodyHtmlText不存在的时候,访问BodyHtmlText会出现 }
写入数据库处理,调用我们通用处理类处理数据信息的存储即可。
#region 写入邮件信息到数据库 int mailId = -1; try { mailId = BLLFactory<MailReceive>.Instance.Insert2(receiveInfo); } catch (Exception ex) { error = string.Format("写入邮件信息到数据库出错:账号{0} 邮件标题:{1}", username, mime_message.Subject); LogTextHelper.Error(error, ex); } if (mailId <= 0) return; //如果邮件没有保存,不要保存附件 #endregion
2、邮件的附件处理
邮件的附件,包含常规的邮件附件,以及嵌入正文的附件图片,因此需要进行不同类型的判断,并一起把附件获取下来存储,这样在显示的时候,才能正常显示相关的附件。
其中Mail_Message 对象有一个函数,可以获取全部这两类附件的信息到列表中。
public MIME_Entity[] GetAttachments(bool includeInline, bool includeEmbbedMessage)
这样我们来调用这个函数,然后进行附件的提取存储处理即可。
#region 邮件附件内容 foreach (var entity in mime_message.GetAttachments(true, true)) { string fileName = ""; #region 判断是普通附件还是嵌入的内容附件 if (entity.ContentDisposition != null && entity.ContentDisposition.DispositionType == MIME_DispositionTypes.Attachment) { Console.WriteLine("Attachment: " + entity.ContentDisposition.Param_FileName); fileName = entity.ContentDisposition.Param_FileName; } else { string cid = entity.ContentID.Substring(1, entity.ContentID.Length - 2); if (entity.ContentType.Param_Name != null && mime_message.BodyHtmlText.Contains(string.Format("cid:{0}", cid))) { Console.WriteLine("Embeded image: " + cid); fileName = cid; } else { Console.WriteLine("Unknown attachment."); } }
邮件的附件信息,entity对象需要转换为MIME_b_SinglepartBase进行处理的。
var byteObj = entity.Body as MIME_b_SinglepartBase;
因此我们可以通过文件方式存储它的字节数据,如下所示。
File.WriteAllBytes(filename, byteObj.Data);
或者调用附件信息进行存储处理(可以是本地存储、或者FTP上传等方式)
如对于测试带有嵌入图片,附件信息的邮件,这样处理能够顺利获取所有的附件信息。
因此可以使用邮件管理模块中的定时收发邮件的处理,实现邮件的接收和发送。
3、163邮箱对于IMAP协议不支持
在测试IMAP协议收取邮件的时候,对于POP3发现大多数邮箱都是支持的。
但虽然163邮箱对POP3的支持不错,对IMAP协议却不支持,都是使用授权码进行登录,也确实登录成功了,但是IMAP协议切换邮箱进行邮件收取的时候,就会提示
提示错误信息。
00023 NO SELECT Unsafe Login. Please contact kefu@188.com for help
如有兴趣,了解Lumisoft.NET组件的相关使用内容,请参考我相关随笔,谢谢。
《 基于Lumisoft.NET组件和.NET API实现邮件发送功能的对比》
转载请注明出处:撰写人:伍华聪 http://www.iqidi.com