如何比OpenPop.dll更快地解析电子邮件

时间:2014-06-23 11:09:56

标签: c# .net parsing email pop3

可以使用OpenPop.dll。

    Pop3Client objPOP3Client = new Pop3Client();
    int intTotalEmail = 0;
    DataTable dtEmail = new DataTable();
    object[] objMessageParts;

    try
    {
        dtEmail = GetAllEmailStructure();

        if (objPOP3Client.Connected)
            objPOP3Client.Disconnect();

        objPOP3Client.Connect(strHostName, intPort, bulUseSSL);
        try
        {
            objPOP3Client.Authenticate(strUserName, new Common()._Decode(strPassword));
            intTotalEmail = objPOP3Client.GetMessageCount();

            AddMapping();

            for (int i = 1; i <= intTotalEmail; i++)
            {
                objMessageParts = GetMessageContent(i, ref objPOP3Client, dtExistMailList);

                if (objMessageParts != null && objMessageParts[0].ToString() == "0")
                {
                    AddToDtEmail(objMessageParts, i, dtEmail, dtUserList, dtTicketIDList, dtBlacklistEmails, dtBlacklistSubject, dtBlacklistDomains);
                }
            }
        }
        catch (Exception ex)
        {
        }
    }
    catch (Exception ex)
    {
        ParserLogError(ex, "GetAllEmail()");
    }
    finally
    {
        if (objPOP3Client.Connected)
            objPOP3Client.Disconnect();
    }

    // function

     public object[] GetMessageContent(int intMessageNumber, ref Pop3Client objPOP3Client, DataTable dtExistingMails)
  {
    object[] strArrMessage = new object[10];
    Message objMessage;
    MessagePart plainTextPart = null, HTMLTextPart = null;
    string strMessageId = "";

    try
    {
        strArrMessage[0] = "";
        strArrMessage[1] = "";
        strArrMessage[2] = "";
        strArrMessage[3] = "";
        strArrMessage[4] = "";
        strArrMessage[5] = "";
        strArrMessage[6] = "";
        strArrMessage[7] = null;
        strArrMessage[8] = null;
        strArrMessage[7] = "";
        strArrMessage[8] = "";

        objMessage = objPOP3Client.GetMessage(intMessageNumber);
        strMessageId = (objMessage.Headers.MessageId == null ? "" : objMessage.Headers.MessageId.Trim());

        if (!IsExistMessageID(dtExistingMails, strMessageId)) //check in data base message id is exists or not 
        {
            strArrMessage[0] = "0";
            strArrMessage[1] = objMessage.Headers.From.Address.Trim();     // From EMail Address
            strArrMessage[2] = objMessage.Headers.From.DisplayName.Trim(); // From EMail Name
            strArrMessage[3] = objMessage.Headers.Subject.Trim();// Mail Subject     
            plainTextPart = objMessage.FindFirstPlainTextVersion();
            strArrMessage[4] = (plainTextPart == null ? "" : plainTextPart.GetBodyAsText().Trim());
            HTMLTextPart = objMessage.FindFirstHtmlVersion();
            strArrMessage[5] = (HTMLTextPart == null ? "" : HTMLTextPart.GetBodyAsText().Trim());
            strArrMessage[6] = strMessageId;
            List<MessagePart> attachment = objMessage.FindAllAttachments();
            strArrMessage[7] = null;
            strArrMessage[8] = null;
            if (attachment.Count > 0)
            {
                if (attachment[0] != null && attachment[0].IsAttachment)
                {
                    strArrMessage[7] = attachment[0].FileName.Trim();
                    strArrMessage[8] = attachment[0];
                }
            }
        }
        else
        {
            strArrMessage[0] = "1";
        }
    }
    catch (Exception ex)
    {
        ParserLogError(ex, "GetMessageContent()");
    }
    return strArrMessage;
 }

但是,我想让它比OpenPop.dll更快。所以如果有任何其他技术可以解析邮件,请告诉我。

请检查代码,然后告诉我。

提前致谢

1 个答案:

答案 0 :(得分:6)

  

但是,我想让它比OpenPop.dll更快。所以请让我   知道是否有任何其他技术可用于解析邮件。

在你的GetMessageContent()方法中,消耗大量时间的地方是:

objMessage = objPOP3Client.GetMessage(intMessageNumber);

下载消息的网络I / O部分无法真正优化,但OpenPop.NET的解析器速度很慢(基于我自己的性能测试)。

MimeKit在解析电子邮件时比OpenPop.NET快25倍。

OpenPop.NET的MIME解析器中的一个主要性能问题是它使用StreamReader进行解析(由于不必要的字符集转换速度慢,一次读取1行等等 - 我对另一个进行了分析使用StreamReader进行解析的电子邮件库:https://stackoverflow.com/a/18787176/87117)。

然后问题是OpenPop.NET的解析器在解析/解码之前还使用Regex从头字符串中删除CFWS(注释和折叠空格)。这很贵。编写一个可以处理CFWS的良好标记器会好得多。

如果您对我用于优化MimeKit的其他一些技术感兴趣(比高度优化的C实现快或快),我写了一些关于此的博客文章:

Optimization Tricks used by MimeKit: Part 1

我在第1部分中讨论的优化摘要是替换这样的循环来扫描一行的结尾:

while (*inptr != (byte) '\n')
    inptr++;

使用更快的循环,如下所示:

int* dword = (int*) inptr;

do {
    mask = *dword++ ^ 0x0A0A0A0A;
    mask = ((mask - 0x01010101) & (~mask & 0x80808080));
} while (mask == 0);

inptr = (byte*) (dword - 1);
while (*inptr != (byte) '\n')
    inptr++;

将性能提高了20%(尽管在非x86架构上,它需要'dword'为4字节对齐)。

Optimization Tricks used by MimeKit: Part 2

在第2部分中,我将讨论编写更优化的System.IO.MemoryStream版本。 MemoryStream的问题在于它必须在内容中保留1个连续的内存块,这意味着当您向其写入更多数据并且必须调整其内部字节数组的大小时,它必须将内容复制到新数组(这是昂贵的,特别是一旦流中的数据量很大)。

为了解决这个性能瓶颈,我编写了一个不需要使用连续内存块的MemoryBlockStream - 它使用了一个字节数组的链表。当你溢出当前缓冲区时,它不必调整字节数组的大小,而是简单地分配另一个2048字节的数组,数据将溢出并附加到链表中。

注意:MimeKit本身只进行电子邮件解析,不执行POP3或SMTP或IMAP。如果你想要那种功能,我还写了一个基于MimeKit构建的库,它也是这样做的:MailKit

<强>更新

使用MailKit(根据要求)下载/解析所有邮件的示例代码:

using System;
using System.Net;

using MailKit.Net.Pop3;
using MailKit;
using MimeKit;

namespace TestClient {
    class Program
    {
        public static void Main (string[] args)
        {
            using (var client = new Pop3Client ()) {
                client.Connect ("pop.gmail.com", 995, true);

                // Note: since we don't have an OAuth2 token, disable
                // the XOAUTH2 authentication mechanism.
                client.AuthenticationMechanisms.Remove ("XOAUTH2");

                client.Authenticate ("joey@gmail.com", "password");

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

                client.Disconnect (true);
            }
        }
    }
}