经过大量工作和JavaMail员工的帮助,这种“缓慢”的来源来自API中的FETCH行为。确实,正如pjaol所说,每次需要消息的信息(标头或消息内容)时,我们都会返回服务器。
如果FetchProfile允许我们批量获取许多消息的标头信息或标志,则无法直接获取多个消息的内容。
幸运的是,我们可以编写自己的IMAP命令来避免这种“局限性”(这样做是为了避免出现内存不足错误:在一个命令中提取内存中的每个邮件可能会很繁重)。
这是我的代码:
import com.sun.mail.iap.Argument;import com.sun.mail.iap.ProtocolException;import com.sun.mail.iap.Response;import com.sun.mail.imap.IMAPFolder;import com.sun.mail.imap.protocol.BODY;import com.sun.mail.imap.protocol.FetchResponse;import com.sun.mail.imap.protocol.IMAPProtocol;import com.sun.mail.imap.protocol.UID;public class CustomProtocolCommand implements IMAPFolder.ProtocolCommand { int start; int end; public CustomProtocolCommand(int start, int end) { this.start = start; this.end = end; } @Override public Object doCommand(IMAPProtocol protocol) throws ProtocolException { Argument args = new Argument(); args.writeString(Integer.toString(start) + ":" + Integer.toString(end)); args.writeString("BODY[]"); Response[] r = protocol.command("FETCH", args); Response response = r[r.length - 1]; if (response.isOK()) { Properties props = new Properties(); props.setProperty("mail.store.protocol", "imap"); props.setProperty("mail.mime.base64.ignoreerrors", "true"); props.setProperty("mail.imap.partialfetch", "false"); props.setProperty("mail.imaps.partialfetch", "false"); Session session = Session.getInstance(props, null); FetchResponse fetch; BODY body; MimeMessage mm; ByteArrayInputStream is = null; // last response is only result summary: not contents for (int i = 0; i < r.length - 1; i++) { if (r[i] instanceof IMAPResponse) { fetch = (FetchResponse) r[i]; body = (BODY) fetch.getItem(0); is = body.getByteArrayInputStream(); try { mm = new MimeMessage(session, is); Contents.getContents(mm, i); } catch (MessagingException e) { e.printStackTrace(); } } } } // dispatch remaining untagged responses protocol.notifyResponseHandlers(r); protocol.handleResult(response); return "" + (r.length - 1); }}getContents(MimeMessage mm,int i)函数是一个经典函数,该函数以递归方式将消息的内容打印到文件中(网上有许多示例)。
为了避免出现内存不足错误,我只需设置maxDocs和maxSize限制(这是任意完成的,可能可以改进!),其用法如下:
public int efficientGetContents(IMAPFolder inbox, Message[] messages) throws MessagingException { FetchProfile fp = new FetchProfile(); fp.add(FetchProfile.Item.FLAGS); fp.add(FetchProfile.Item.ENVELOPE); inbox.fetch(messages, fp); int index = 0; int nbMessages = messages.length; final int maxDoc = 5000; final long maxSize = 100000000; // 100Mo // Message numbers limit to fetch int start; int end; while (index < nbMessages) { start = messages[index].getMessageNumber(); int docs = 0; int totalSize = 0; boolean noskip = true; // There are no jumps in the message numbers // list boolean notend = true; // Until we reach one of the limits while (docs < maxDoc && totalSize < maxSize && noskip && notend) { docs++; totalSize += messages[index].getSize(); index++; if (notend = (index < nbMessages)) { noskip = (messages[index - 1].getMessageNumber() + 1 == messages[index] .getMessageNumber()); } } end = messages[index - 1].getMessageNumber(); inbox.doCommand(new CustomProtocolCommand(start, end)); System.out.println("Fetching contents for " + start + ":" + end); System.out.println("Size fetched = " + (totalSize / 1000000) + " Mo"); } return nbMessages;}在此不要使用我所使用的消息号,它是不稳定的(如果从服务器中删除了消息,则这些更改)。更好的方法是使用UID!然后,将命令从FETCH更改为UID
FETCH。
希望这会有所帮助!



