C# 从 Windows 服务中保存 Exchange .EML 文件

标签 c# service save exchange-server eml

我目前正在编写一个 Windows 服务来登录特定的 Exchange 帐户、获取新电子邮件、解析它们并将电子邮件保存在适当的文件夹中。

除了保存电子邮件之外,一切都运行良好。

相关代码块(try/catch block 和删除不相关的内容以保持简短):-

设置服务

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials(emailAddress, password);
service.AutodiscoverUrl(emailAddress, RedirectionUrlValidationCallback);
CheckForNewEmails(service);

获取和检查新电子邮件

private static void CheckForNewEmails(ExchangeService service)
{
    int offset = 0;
    int pageSize = 50;
    bool more = true;
    ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
    view.PropertySet = PropertySet.IdOnly;
    FindItemsResults<Item> findResults;
    List<EmailMessage> emails = new List<EmailMessage>();

    while (more)
    {
        findResults = service.FindItems(WellKnownFolderName.Inbox, view);
        foreach (var item in findResults.Items)
        {
            emails.Add((EmailMessage)item);
        }
        more = findResults.MoreAvailable;
        if (more)
        {
            view.Offset += pageSize;
        }
    }

    if (emails.Count > 0)
    {
        PropertySet properties = (BasePropertySet.FirstClassProperties);
        service.LoadPropertiesForItems(emails, properties);
        var mailItems = new List<DatabaseService.MailItem>();
        var dbService = new DatabaseService();
        var defaultUser = dbService.GetDefaultUser(defaultUserID);

        foreach (var email in emails)
        {
            var mailItem = new DatabaseService.MailItem();
            mailItem.mail = email;
            mailItem.MessageID = email.InternetMessageId;
            mailItem.Sender = email.Sender.Address;
            dbService.FindLinks(service, ref mailItem, defaultUser);
            mailItems.Add(mailItem);
            LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
                                         mailItem.MessageID ,
                                         email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
                                         email.Sender,
                                         email.Subject,
                                         mailItem.Hash,
                                         mailItem.LinkString
                                         ));
        }
    }
}

查找应该链接到的人

public void FindLinks(ExchangeService service, ref MailItem mailItem, User defaultUser)
{
    string address = mailItem.Sender;

    // get file hash
    var tempPath = Path.GetTempPath();
    var fileName = GetFilenameFromSubject(mailItem);
    var fullName = Path.Combine(tempPath, fileName);
    SaveAsEML(service, mailItem, fullName);
    var sha = new SHA256Managed();
    mailItem.Hash = Convert.ToBase64String(sha.ComputeHash(File.OpenRead(fullName)));
    File.Delete(fullName);

    using (var db = DatabaseHelpers.GetEntityModel())
    {
        // Do all the linking stuff
    }
}

最后,问题区域:将文件保存到磁盘(在本例中保存到临时文件夹,以便我可以获得文件哈希,以检查它不是重复的(例如抄送等))

查看 StackOverflow 和各种其他来源,似乎有两种方法可以做到这一点:-

1)使用MailItem.Load方法

private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
    using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
    {
        mailItem.mail.Load(new PropertySet(ItemSchema.MimeContent));
        fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
    }
}

使用上面的代码,文件已创建并具有正确的内容。

但是,在此之后尝试访问任何电子邮件属性都会导致空异常崩溃(也是大崩溃,没有 Try/Catch 或 UnhandledException 陷阱会拾取它,只会杀死服务)

在上面的代码中,这一行使整个服务崩溃:-

LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
               mailItem.MessageID ,
               email.DateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
               email.Sender,
               email.Subject,
               mailItem.Hash,
               mailItem.LinkString
));

具体来说,引用 email.DateTimeSent

我直接在此行之前添加了一些诊断代码:-

if (email == null) { Log it's null; } else { Log NOT null;}
if (email.DateTimeSent == null) { Log it's null; } else { Log NOT null; }

第一行记录 NOT null,因此电子邮件仍然存在。

但是,第二行立即崩溃并出现空异常错误,没有记录任何内容。

如果我注释掉 SaveAsEML(service, mailItem, fullName); 行从 FindLinks 则一切正常(当然文件未保存除外)。

2) 绑定(bind)属性集

private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
{
    using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
    {
        PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
        var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
        fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
    }
}

这样做,不会崩溃,它会很好地处理每封电子邮件,并且可以引用 email.DateTimeSent 和所有其他属性。

不幸的是,它创建零长度文件,没有内容。

为此我已经用头撞墙几个小时了(我花了一个小时在各处添加诊断,只是为了追踪引用属性时发生的崩溃),这无疑是我忽略的一件小事,所以如果某种如果灵魂能指出我的愚蠢,我将不胜感激!

编辑:

通过在使用属性之前保存属性的值,我可以轻松地解决上述问题:-

foreach (var email in emails)
{
    var dateTimeSent = email.DateTimeSent;
    var sender = email.Sender;
    var subject = email.Subject;
    var mailItem = new DatabaseService.MailItem();
    mailItem.mail = email;
    mailItem.MessageID = email.InternetMessageId;
    mailItem.Sender = email.Sender.Address;
    dbService.FindLinks(service, ref mailItem, defaultUser);
    mailItems.Add(mailItem);
    LogMessage += (string.Format("Message ID : {1}{0}Sent : {2}{0}From : {3}{0}Subject : {4}{0}Hash : {5}{0}{6}{0}{0}", Environment.NewLine,
                                 mailItem.MessageID,
                                 dateTimeSent.ToString("dd/MM/yyyy hh:mm:ss"),
                                 sender,
                                 subject,
                                 mailItem.Hash ?? "No Hash",
                                 mailItem.LinkString ?? "No Linkstring"
                                 ));
}

这使我能够使用保存文件的 MailItem.Load 方法,从而执行我在这种特定情况下所执行的操作。

但是,该服务还需要执行其他操作,而且我真的不想保存我需要访问的每个属性的副本。

最佳答案

失败的原因

    private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
    {
        using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
        {
            PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
            var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
            fileStream.Write(mailItem.mail.MimeContent.Content, 0, mailItem.mail.MimeContent.Content.Length);
        }
    }

您是否已使用“绑定(bind)”来加载电子邮件变量中包含 MimeContent 的消息,但您尚未使用它。 mailItem.mail 根本不会链接到作为此操作的一部分创建的电子邮件变量(即使它们是服务器上的同一对象)。在本地,这些只是两个单独的变量。 EWS 是客户端/服务器,因此您发出请求,托管 API 将返回表示操作结果的本地对象。但是该对象已断开连接,因此当您执行上面的绑定(bind)时,它只会生成另一个客户端对象来表示该操作的结果。例如,上面应该是

     private string SaveAsEML(ExchangeService service, MailItem mailItem, string savePath)
    {
        using (FileStream fileStream = File.Open(savePath, FileMode.Create, FileAccess.Write))
        {
            PropertySet props = new PropertySet(EmailMessageSchema.MimeContent);
            var email = EmailMessage.Bind(service, mailItem.mail.Id, props);
            fileStream.Write(email.MimeContent.Content, 0, email .MimeContent.Content.Length);
        }
    }

关于C# 从 Windows 服务中保存 Exchange .EML 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52371449/

相关文章:

c# - Azure 服务结构实例计数

javascript - 开发一个将保存在本地的设置页面

java - 是否可以一次性将一组对象插入 DynamoDB - Java

c# - MVVM中事件的处理方式

c# - 从十六进制中获取适当的值

java - 应用程序崩溃后服务无法继续运行

python - 使用带有标题的 np.savetxt 保存结构化的 numpy 数组

c# - 确定 DateTime 是否在给定的日期范围内

javascript - 使用悬停事件发送垃圾邮件时元素可见性问题

python - 来自 Python 服务的警报弹出窗口