【问题标题】:How to download only new emails from imap?如何仅从 imap 下载新电子邮件?
【发布时间】:2011-06-11 03:17:09
【问题描述】:

我有一个用于使用 imap 归档电子邮件的应用程序。此应用程序中还有许多需要存档的 imap 帐户。

此时,应用程序会不时连接到 imap 帐户并仅下载新电子邮件。我的问题是,每次连接到 imap 帐户时,它都会验证所有文件夹中的所有电子邮件并仅下载尚未下载的电子邮件(我为所有电子邮件存储 Message-ID 并仅下载具有 Message-ID 的电子邮件不存储)。 所以我想知道是否有替代方案,因为验证所有电子邮件需要一些时间(10-20K 需要 2-5 分钟)。

我使用 JavaMail API 连接到 imap 帐户。

【问题讨论】:

    标签: java imap jakarta-mail


    【解决方案1】:

    javadoc 有帮助:

    IMAPFolder 提供了方法:

    getMessagesByUID(long start, long end) 和

    getUID(消息消息)

    使用 getUID() 可以获取已下载的最后一条消息的 UID。使用 getMessagesByUID,您可以将下载的最后一条消息定义为起始范围,并使用 getUIDNext() 方法查找最后一条消息,该消息将是范围的结束。

    【讨论】:

    • 对我来说不是一个解决方案,因为 UID 会随着时间而改变,这就是我使用 Message-ID 来跟踪下载的电子邮件的原因。
    • 据我了解 getUID(Message message) 方法,您可以从服务器获取给定消息的实际 MessageUID。因此,可以获取您拥有的最后一条消息的实际 UID,然后使用 getUIDNext() 和 getMessagesByUID() 方法查找最后一条消息并下载给定范围。
    • 当邮件从一个文件夹(或 IMAP 术语中的“邮箱”)移动到另一个文件夹时,邮件 UID 将发生变化。 "Unique identifiers are assigned in a strictly ascending fashion in the mailbox; as each message is added to the mailbox it is assigned a higher UID than the message(s) which were added previously." telebog 似乎希望每条消息只下载一次,无论它在哪个文件夹中。
    • 嗯...我可以存储每个文件夹中最后下载的电子邮件的 UID,而不是从一个文件夹中获取所有消息,我使用 getMessagesByUID 只收到新消息,但在下载新电子邮件之前,我必须验证如果尚未使用 Message-ID 下载它。谢谢。
    • 我永远不会单独信任 Message-ID(可以由发件人设置为任何值),因为它不能是唯一的。使用(如果可用)消息 ID、发件人地址和电子邮件时间来识别消息。您可以将它们写入字符串并计算 MD5-Hash。因此,您将拥有每封电子邮件的唯一标识符。
    【解决方案2】:

    只检查标题,当你到达一个已知的(最后一个已知的)时,跳出:

    例如(我今天感觉特别好),这是实际生产代码中的一个例外(某些部分已被剪切,因此它可能无法编译,state.processed 是一些集合,最好是 LinkedHashMap surrogate [keySet()](并且 w/一些最大边界 boolean removeEldestEntry())

     try {
          store = mailSession.getStore("imap");
          try {
            store.connect();
            Folder folder = store.getFolder("INBOX");
            folder.open(Folder.READ_ONLY);
    
            int count = folder.getMessageCount();
            for(int localProc=0, chunk=49;localProc<10 && count>0; count -=chunk+1){
    
    
              Message messages[] = folder.getMessages(Math.max(count-chunk, 1), count);
    
              FetchProfile fp = new FetchProfile();
              fp.add(FetchProfile.Item.ENVELOPE);
              fp.add("Message-ID");
    //add more headers, if need be
              folder.fetch(messages,fp);
    
              for (int i=messages.length;--i>=0;) {
    
                //can check abort request here
                Message  message = messages[i];
    
    
                String msgId = getHeader(message,"Message-ID");
                if (msgId!=null && !state.processed.add(msgId)){            
                  if (++localProc>=10){
                    break;
                  }
                  continue;
                }
    ///process here, catch exception, etc..
              }
            }
    
            folder.close(false);        
          } catch (MessagingException e) {
            logger.log(Level.SEVERE, "Mail messaging exception", e);
          }
        } catch (NoSuchProviderException e) {
          logger.log(Level.SEVERE, "No mail provider", e);
        }
    
        if(store != null) {
          try {
            store.close();
          } catch (MessagingException e) {}
        }
    

    【讨论】:

    • 这有点冒险,因为在一堆新消息之上大量已知消息会导致您错过新消息。
    • 没错,这是一个好点;该代码用于读取失败的邮件,所以我想这本身就不是问题。代码应至少保留以前的计数并尝试检索至少那么多消息。
    【解决方案3】:

    过滤 SEEN 标志。此标志用于查找新消息。需要注意的是,如果您的用户正在使用多个阅读器,那么可能已经看到它使用了另一个阅读器。

    【讨论】:

    • 您的意思是RECENT 标志。是的,任何其他客户端与该文件夹的连接都将取消该文件夹中所有邮件的RECENT。
    • 我已经看到它被记录为 SEEN,但在您的实现中它可能是最近的。 IMAP 跟踪客户端尚未读取的消息。对于阅读器界面,我希望我使用任何客户端阅读的任何内容都会在我的界面中标记为已读。在这种情况下,我的背景更多是管理服务器而不是编写客户端。
    • Seen 标志是标准标志。 Java 将在检索到消息时自动将其标记为已看到。这是我期望所有客户的行为。可以清除标志。
    【解决方案4】:

    作为标头一部分的消息 ID 始终是唯一的,即使您手动设置它也是如此。我已经使用 gamil 和 racksoace 对其进行了测试。

    【讨论】:

    • 但这不是必需的,而且很容易艾米根本不存在。在 Gmail 上也是如此。
    猜你喜欢
    • 1970-01-01
    • 2011-09-06
    • 2023-03-19
    • 1970-01-01
    • 2011-12-21
    • 1970-01-01
    • 2023-03-20
    • 2014-03-18
    • 2011-04-06
    相关资源
    最近更新 更多