【问题标题】:How can I reduce EWS calls when downloading attachments from an Inbox?从收件箱下载附件时,如何减少 EWS 调用?
【发布时间】:2015-11-24 15:58:26
【问题描述】:

我在 StackO 的大力帮助下编写了此方法,但我一直无法找到改进此方法的方法。

现在,我遍历 Office 365 收件箱中的每封邮件,遍历其附件集合,并将每个文件保存到磁盘(如果它是 Excel 工作簿)。这目前正在运行,但会导致大量调用 Exchange。收件箱的大小可能会变得非常大,因此这需要很长时间才能运行,每个呼叫的时间大约为 0.5 秒。

如果我数对了,调用次数为 (n / 100) + 2n。

  • n 是收件箱中的消息数(每条消息 2 次调用)
  • 100 是 pageSize,因为我没有见过比这更大的东西(每页调用一次)

注意:通常 (99.9999%) 每封邮件只有一个附件,但我为 CYA 目的添加了内部循环。可能是一些内存开销,但几乎没有缩放因子。

我想知道是否有更好的方法,减少 Web 服务调用。是否有可以批量下载附件的 EWS 方法?我对 EWS 很陌生,所以我确定我在这里缺乏理解。 我要做的是将pageSizeLoad(path)这批文件减少到磁盘,减少规模。

对于以下代码的任何改进建议,关于减少 EWS 调用,我们将不胜感激。

public void SaveAttachmentsFromInbox(string[] extensionFilter = null)
    {

        // Default to excel files
        extensionFilter = extensionFilter ?? new[] { ".xls", ".xlsx" };

        // Config for traversing inbox
        int offset = 0;
        int pageSize = 100;
        ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
        view.PropertySet = PropertySet.FirstClassProperties;
        FindItemsResults<Item> findResults;

        // Loop through the inbox
        //   and save all attachments of the designated file types
        bool more = true;
        var fileCount = 0;
        while (more)
        {

            findResults = service.FindItems(WellKnownFolderName.Inbox, view);
            // Load each sheet's data into an Object
            foreach (var item in findResults.Items)
            {
                //get FirstClassProperties
                item.Load(view.PropertySet);

                string vendor = GetVendor(EmailMessage.Bind(service, item.Id));
                messageIds.Add(item.Id.ToString());

                // Save files to disk
                foreach (FileAttachment file in item.Attachments)
                {
                    string fileExtension = file.Name.Substring(file.Name.IndexOf('.'), file.Name.Length - file.Name.IndexOf('.'));

                    if (extensionFilter.Contains(fileExtension))
                    {
                        var fullPath = Path.Combine(path, file.Name);
                        attachmentInfo.Add(fullPath, vendor);

                        // Loads attachment and saves to disk
                        file.Load(fullPath);

                        fileCount++;
                        Console.Write("\rFiles received... {0}    ", fileCount);
                    }
                }
            }



            Console.WriteLine(); // Next line

            more = findResults.MoreAvailable;
            // Page through inbox if more messages remain
            if (more)
            {
                view.Offset += pageSize;
            }
        }
        Console.WriteLine(attachmentInfo.Count + " Excel Attachment Downloads successful.\n");
    }

【问题讨论】:

    标签: c# exchangewebservices ews-managed-api


    【解决方案1】:

    除了使用 AQS 减少从服务器返回的记录集外,您还应该使用批处理命令首先使用 LoadPropertiesForItems 获取 Item 属性(替换代码中每个项目的 Bind),然后您可以使用 GetAttachments 批量下载附件(您需要确保使用 EWS 托管 API 的 2.2 版)这将意味着例如一批 100 个项目您进行一个 FindItems 调用、一个批量 GetItem 调用和一个批量 GetAttachment 调用,而不是 1 * 100 * 100 如果您使用绑定和加载。例如像

             ItemView ivItemView = new ItemView(100);
            PropertySet flLevel = new PropertySet(BasePropertySet.IdOnly);
            ivItemView.PropertySet = flLevel;
            FindItemsResults<Item> faItems = service.FindItems(WellKnownFolderName.Inbox, "attachment:.xlsx OR attachment:xls", ivItemView);
            PropertySet slLevel = new PropertySet(BasePropertySet.FirstClassProperties);
            if (faItems.Items.Count > 0)
            {
                service.LoadPropertiesForItems(faItems, slLevel);
            }
            List<Attachment> atAttachments = new List<Attachment>();
            foreach (Item itItem in faItems.Items)
            {
                foreach (Attachment atAttachment in itItem.Attachments)
                {
                    if (atAttachment is FileAttachment)
                    {
                        string fileExtension = atAttachment.Name.Substring(atAttachment.Name.IndexOf('.'), atAttachment.Name.Length - atAttachment.Name.IndexOf('.'));
                        if (extensionFilter.Contains(fileExtension))
                        {
                            atAttachments.Add(atAttachment);
    
                        }
                    }
                }
            }
            service.GetAttachments(atAttachments.ToArray(), BodyType.HTML,null);
            foreach (FileAttachment FileAttach in atAttachments)
            {
                Console.Write(FileAttach.Name);
                System.IO.File.WriteAllBytes("c:\\export\\" + FileAttach.Name, FileAttach.Content);
                //save off
            }
    

    【讨论】:

    • 这就是我要找的。谢谢!
    【解决方案2】:

    如果您的目标是 Exchange 2010 或更高版本,您可以使用FindItem with Advanced Query Syntax

    ItemView view = new ItemView(100);
    FindItemsResults<Item> results = service.FindItems(folder, "Has attachment:true", view);
    
    foreach (Item item in results.Items)
    {
      if (item is EmailMessage)
        {
          // Get the item and FileAttachments in the same way.
        }
    }
    

    我自己没有尝试过,但是使用 AQS Has attachment:true AND .xlsx 可能会获得更好的结果。

    【讨论】:

    • 我喜欢这个。不知道AQS。附件是否可用而无需再次Load() 每个单独的电子邮件项目?这看起来类似于我现在正在做的事情,在一天结束时:Load() 消息到内存,然后Load(path) 附件到磁盘。
    • 我不记得我害怕。不过,我相当肯定你仍然需要Get 你在代码中所做的每个结果。
    • 有没有人尝试使用查询字符串中的附件扩展名并让它工作? - 因为我似乎无法让它工作,如果它甚至应该这样做的话。
    猜你喜欢
    • 2017-03-15
    • 1970-01-01
    • 2017-09-21
    • 2012-11-28
    • 1970-01-01
    • 1970-01-01
    • 2022-09-29
    • 1970-01-01
    • 2018-08-18
    相关资源
    最近更新 更多