POP3、IMAP4和Exchange收取邮件的JAVA开发详解(二)——代码解析和详细开发过程

上一篇讲到了构建接口和各种需要用到的实体类,这一篇会讲到业务实现类和邮件解析工具类的实现。我会在每一个类前面或注释写上一些需要注意的地方。本地存储路径会保存一份邮件源码eml,还有解析出来的各种附件。

1.POP邮件拉取

POP3协议只能获取到收件箱(INBOX)文件夹里的邮件

/** * pop3协议邮箱收件类 * * @author zeemoo * @date 2019/01/18 */
public class Pop3Service implements IMailService {
    /** * Session properties的键名 */
    private static final String PROPS_HOST = "mail.pop3.host";
    private static final String PROPS_PORT = "mail.pop3.port";
    private static final String PROPS_SSL = "mail.pop3.ssl.enable";
    private static final String PROPS_AUTH = "mail.pop3.auth";
    private static final String PROPS_SOCKS_PROXY_HOST = "mail.pop3.socks.host";
    private static final String PROPS_SOCKS_PROXY_PORT = "mail.pop3.socks.port";
    private static final String PROPS_HTTP_PROXY_HOST = "mail.pop3.proxy.host";
    private static final String PROPS_HTTP_PROXY_PORT = "mail.pop3.proxy.port";
    private static final String PROPS_HTTP_PROXY_USER = "mail.pop3.proxy.user";
    private static final String PROPS_HTTP_PROXY_PASSWORD = "mail.pop3.proxy.password";
    /** * POP3只能打开INBOX文件夹,也就是收件箱 */
    private static final String FOLDER_INBOX = "INBOX";
    /** * 一次性最多能同步的数量 */
    private static final int MAX_SYNCHRO_SIZE = 80;

    /** * 解析邮件 * * @param mailItem 需要解析的邮件列表项 * @param localSavePath 本地存储路径 * @return * @throws MailPlusException */
    @Override
    public UniversalMail parseEmail(MailItem mailItem, String localSavePath) throws MailPlusException {
        //使用通用的邮件解析工具类解析邮件
        return MailItemParser.parseMail(mailItem, localSavePath);
    }

    /** * 列举需要被同步的邮件 * * @param mailConn 邮箱连接,也可以做成字段 * @param existUids 已存在的邮件uid * @return * @throws MailPlusException */
    @Override
    public List<MailItem> listAll(MailConn mailConn, List<String> existUids) throws MailPlusException {
        POP3Store pop3Store = mailConn.getPop3Store();
        try {
            //获取文件夹,POP3只能获取收件箱的邮件
            POP3Folder folder = (POP3Folder) pop3Store.getFolder(FOLDER_INBOX);
            //文件夹必须打开才可以获取邮件
            folder.open(Folder.READ_ONLY);
            Message[] messages = folder.getMessages();
            List<MailItem> mailItems = new ArrayList<>();
            //进行去重筛选需要同步的邮件
            for (int i = messages.length - 1; i >= 0; i--) {
                String uid = folder.getUID(messages[i]);
                if (!existUids.contains(uid)) {
                    POP3Message pop3Message = (POP3Message) messages[i];
                    mailItems.add(MailItem.builder().pop3Message(pop3Message).build());
                }
                //到一定数量停止
                if (mailItems.size() == MAX_SYNCHRO_SIZE) {
                    break;
                }
            }
            return mailItems;
        } catch (MessagingException e) {
            e.printStackTrace();
            throw new MailPlusException(String.format("【POP3服务】打开文件夹/获取邮件列表失败,错误信息【{}】"));
        }
    }

    /** * 连接服务器 * * @param mailConnCfg 连接配置 * @param proxy 是否设置代理 * @return 返回连接 */
    @Override
    public MailConn createConn(MailConnCfg mailConnCfg, boolean proxy) throws MailPlusException {
        //构建Session Properties
        Properties properties = new Properties();
        properties.put(PROPS_HOST, mailConnCfg.getHost());
        properties.put(PROPS_PORT, mailConnCfg.getPort());
        properties.put(PROPS_SSL, mailConnCfg.isSsl());
        properties.put(PROPS_AUTH, true);

        //设置代理
        if (proxy && mailConnCfg.getProxyType() != null) {
            ProxyTypeEnum proxyType = mailConnCfg.getProxyType();
            if (proxyType.equals(ProxyTypeEnum.HTTP)) {
                properties.put(PROPS_HTTP_PROXY_HOST, mailConnCfg.getProxyHost());
                properties.put(PROPS_HTTP_PROXY_PORT, mailConnCfg.getProxyPort());
                properties.put(PROPS_HTTP_PROXY_USER, mailConnCfg.getProxyUsername());
                properties.put(PROPS_HTTP_PROXY_PASSWORD, mailConnCfg.getProxyPassword());
            } else if (proxyType.equals(ProxyTypeEnum.SOCKS)) {
                //java mail里socks代理是不支持用户名密码验证的
                properties.put(PROPS_SOCKS_PROXY_HOST, mailConnCfg.getSocksProxyHost());
                properties.put(PROPS_SOCKS_PROXY_PORT, mailConnCfg.getSocksProxyPort());
            }
        }
        //构建session
        Session session = Session.getInstance(properties);
        try {
            //连接
            Store store = session.getStore("pop3");
            store.connect(mailConnCfg.getEmail(), mailConnCfg.getPassword());
            return MailConn.builder().pop3Store((POP3Store) store).build();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        } catch (MessagingException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }
}

2.IMAP邮件拉取

IMAP和POP3差不多,但是IMAP能获取到更多的文件夹。值得注意的是IMAP的邮件uid是数字,针对帐号和文件夹唯一,建议自己重装一下uid,我采用【文件夹+uid】的格式返回,如果你是多用户的一个项目,可以【用户ID+邮箱+文件夹+uid】的格式,或者在这个基础上再md5一下。

/** * IMAP4协议邮箱收取类 * * @author zeemoo * @date 2018/12/8 */
public class ImapService implements IMailService {

    /** * Session properties的键名 */
    private static final String PROPS_HOST = "mail.imap.host";
    private static final String PROPS_PORT = "mail.imap.port";
    private static final String PROPS_SSL = "mail.imap.ssl.enable";
    private static final String PROPS_AUTH = "mail.imap.auth";
    private static final String PROPS_SOCKS_PROXY_HOST = "mail.imap.socks.host";
    private static final String PROPS_SOCKS_PROXY_PORT = "mail.imap.socks.port";
    private static final String PROPS_HTTP_PROXY_HOST = "mail.imap.proxy.host";
    private static final String PROPS_HTTP_PROXY_PORT = "mail.imap.proxy.port";
    private static final String PROPS_HTTP_PROXY_USER = "mail.imap.proxy.user";
    private static final String PROPS_HTTP_PROXY_PASSWORD = "mail.imap.proxy.password";
    private static final String PROPS_PARTIALFETCH = "mail.imap.partialfetch";
    private static final String PROPS_STARTTLS = "mail.imap.starttls.enable";
    /** * 一次性最多同步的邮件数量 */
    private static final int MAX_SYNCHRO_SIZE = 100;

    /** * 解析邮件 * * @param mailItem 邮箱列表项 * @param localSavePath 本地存储路径 * @return * @throws MailPlusException */
    @Override
    public UniversalMail parseEmail(MailItem mailItem, String localSavePath) throws MailPlusException {
        return MailItemParser.parseMail(mailItem, localSavePath);
    }

    /** * 列举需要被同步的邮件 * * @param mailConn 邮箱连接,可以做成这个类的字段 * @param existUids 已同步的邮件uid * @return * @throws MailPlusException */
    @Override
    public List<MailItem> listAll(MailConn mailConn, List<String> existUids) throws MailPlusException {
        IMAPStore imapStore = mailConn.getImapStore();
        try {
            Folder defaultFolder = imapStore.getDefaultFolder();
            List<MailItem> mailItems = new ArrayList<>();
            Folder[] list = defaultFolder.list();
            //判断是否达到一定数量的标志(使用双层循环)
            boolean flag = false;
            for (int i = 0; i < list.length; i++) {
                IMAPFolder imapFolder = (IMAPFolder) list[i];
                //Gmail额外分层
                if (imapFolder.getName().equalsIgnoreCase("[gmail]")) {
                    flag = listGmailMessageFolder(mailItems, existUids, imapFolder);
                } else {
                    flag = listFolderMessage(mailItems, existUids, imapFolder);
                }
                //已达到数目,直接退出循环
                if (flag) {
                    break;
                }
            }
            return mailItems;
        } catch (MessagingException e) {
            e.printStackTrace();
            throw new MailPlusException(String.format("【IMAP服务】打开文件夹/获取邮件列表失败,错误信息【{}】"));
        }
    }

    /** * Gmail邮箱有额外的一层文件夹,需要被再打开一次 * * @param target 存储需要被同步的邮件列表项 * @param existUids 已同步下来的邮件uid * @param imapFolder 有邮件的文件夹 * @return * @throws MessagingException */
    private boolean listGmailMessageFolder(List<MailItem> target, List<String> existUids, IMAPFolder imapFolder) throws MessagingException {
        Folder[] list = imapFolder.list();
        boolean flag = false;
        for (Folder folder :
                list) {
            flag = listFolderMessage(target, existUids, (IMAPFolder) folder);
            if (flag) {
                break;
            }
        }
        return flag;
    }

    /** * 通用的获取文件夹下邮件代码 * * @param target 存储需要被同步的邮件列表项 * @param existUids 已同步下来的邮件uid * @param imapFolder 有邮件的文件夹 * @return * @throws MessagingException */
    private boolean listFolderMessage(List<MailItem> target, List<String> existUids, IMAPFolder imapFolder) throws MessagingException {
        boolean flag = false;
        imapFolder.open(Folder.READ_ONLY);
        Message[] messages = imapFolder.getMessages();
        for (int j = messages.length - 1; j >= 0; j--) {
            if (!existUids.contains(String.valueOf(imapFolder.getFullName() + imapFolder.getUID(messages[j])))) {
                target.add(MailItem.builder().imapMessage((IMAPMessage) messages[j]).build());
            }
            flag = target.size() == MAX_SYNCHRO_SIZE;
            if (flag) {
                break;
            }
        }
        return flag;
    }

    /** * 连接服务器 * * @param mailConnCfg 连接配置 * @param proxy 是否代理 * @return 返回连接 */
    @Override
    public MailConn createConn(MailConnCfg mailConnCfg, boolean proxy) throws MailPlusException {
        //构建Session Properties
        Properties properties = new Properties();
        properties.put(PROPS_HOST, mailConnCfg.getHost());
        properties.put(PROPS_PORT, mailConnCfg.getPort());
        properties.put(PROPS_SSL, mailConnCfg.isSsl());
        properties.put(PROPS_PARTIALFETCH, false);
        properties.put(PROPS_STARTTLS, false);
        properties.put(PROPS_AUTH, true);
        //设置代理
        if (proxy && mailConnCfg.getProxyType() != null) {
            ProxyTypeEnum proxyType = mailConnCfg.getProxyType();
            if (proxyType.equals(ProxyTypeEnum.SOCKS)) {
                properties.put(PROPS_SOCKS_PROXY_HOST, mailConnCfg.getSocksProxyHost());
                properties.put(PROPS_SOCKS_PROXY_PORT, mailConnCfg.getSocksProxyPort());
            } else if (proxyType.equals(ProxyTypeEnum.HTTP)) {
                properties.put(PROPS_HTTP_PROXY_HOST, mailConnCfg.getProxyHost());
                properties.put(PROPS_HTTP_PROXY_PORT, mailConnCfg.getProxyPort());
                properties.put(PROPS_HTTP_PROXY_USER, mailConnCfg.getProxyUsername());
                properties.put(PROPS_HTTP_PROXY_PASSWORD, mailConnCfg.getProxyPassword());
            }
        }
        //构建session
        Session session = Session.getInstance(properties);
        try {
            //连接
            Store store = session.getStore("imap");
            store.connect(mailConnCfg.getEmail(), mailConnCfg.getPassword());
            return MailConn.builder().imapStore((IMAPStore) store).build();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        } catch (MessagingException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }
}

3.Exchange邮件收取

Exchange跟前面的都不一样,使用的是HTTP登录的方式。而且ews的方式和java mail的方式也是很不一样,多是用静态方法的方式去构建。关于服务器地址,Exchange是不需要自己填入服务器地址和端口的,它有一个自动发现的方法。还有就是Exchange的邮件,不管附件是不是在邮件正文里,都会带有cid的值。

/** * Exchange服务 * * @author zeemoo * @date 2019/01/18 */
public class MyExchangeService implements IMailService {

    /** * 最大同步数量 */
    private static final int MAX_SYNCHRO_SIZE = 80;

    /** * 解析邮件 * * @param mailItem * @param localSavePath * @return * @throws MailPlusException */
    @Override
    public UniversalMail parseEmail(MailItem mailItem, String localSavePath) throws MailPlusException {
        return MailItemParser.parseMail(mailItem, localSavePath);
    }

    /** * 列举需要被同步的邮件 * * @param mailConn * @param existUids * @return * @throws MailPlusException */
    @Override
    public List<MailItem> listAll(MailConn mailConn, List<String> existUids) throws MailPlusException {
        ExchangeService exchangeService = mailConn.getExchangeService();
        try {
            Folder msgFolderRoot = Folder.bind(exchangeService, WellKnownFolderName.MsgFolderRoot);
            int childFolderCount = msgFolderRoot.getChildFolderCount();
            FolderView folderView = new FolderView(childFolderCount);
            FindFoldersResults folders = msgFolderRoot.findFolders(folderView);
            ArrayList<Folder> folderList = folders.getFolders();
            List<MailItem> mailItems = new ArrayList<>();
            //判断是否达到一定数量的标志(使用双层循环)
            boolean flag = false;
            for (int i = 0; i < folderList.size(); i++) {
                Folder folder = folderList.get(i);
                String displayName = folder.getDisplayName();
                //排除已知的非邮件格式的文件夹(是EmailMessage类型但是不是标准邮件)
                if (
                        displayName.equals("Files")
                                || displayName.equals("文件")
                                || displayName.equals("檔案")
                                || displayName.equals("記事")
                        ) {
                    continue;
                }
                if (folder.getTotalCount() > 0) {
                    ItemView itemView = new ItemView(folder.getTotalCount());
                    itemView.getOrderBy().add(ItemSchema.DateTimeReceived, SortDirection.Descending);
                    FindItemsResults<Item> items = exchangeService.findItems(folder.getId(), itemView);
                    ArrayList<Item> itemList = items.getItems();
                    for (Item item :
                            itemList) {
                        if (item instanceof EmailMessage && !existUids.contains(item.getId().getUniqueId())) {
                            EmailMessage message = (EmailMessage) item;
                            mailItems.add(MailItem.builder().exchangeMessage(message).build());
                        }
                        flag = mailItems.size() >= MAX_SYNCHRO_SIZE;
                        if (flag) {
                            break;
                        }
                    }
                    if (flag) {
                        break;
                    }
                }
            }
            return mailItems;
        } catch (Exception e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }

    /** * 连接服务器 * * @param mailConnCfg 连接配置 * @param proxy 是否代理 * @return 返回连接 */
    @Override
    public MailConn createConn(MailConnCfg mailConnCfg, boolean proxy) throws MailPlusException {
        ExchangeService service = new ExchangeService();
        //配置代理
        if (proxy) {
            WebProxy webProxy = new WebProxy(
                    mailConnCfg.getHost()
                    , mailConnCfg.getProxyPort()
                    , new WebProxyCredentials(
                    mailConnCfg.getProxyUsername()
                    , mailConnCfg.getProxyPassword()
                    , ""
            )
            );
            service.setWebProxy(webProxy);
        }
        service.setCredentials(
                new WebCredentials(
                        mailConnCfg.getEmail()
                        , mailConnCfg.getPassword()
                )
        );
        //设置超时时间,在拉取邮件的时候保证不中断
        service.setTimeout(600000);
        try {
            if (mailConnCfg.isSsl()) {
                //我怀疑exchange的都是https方式
                service.autodiscoverUrl(mailConnCfg.getEmail(), redirectionUrl -> {
                    return redirectionUrl.toLowerCase().startsWith("https://");
                });
            } else {
                service.autodiscoverUrl(mailConnCfg.getEmail());
            }
            return MailConn.builder().exchangeService(service).build();
        } catch (Exception e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }
}

4.通用邮件解析工具类

直接上代码吧

/** * 通用邮件解析工具 * * @author zeemoo * @date 2019/01/18 */
public class MailItemParser {

    public static final String IMPORT_FOLDER = "手动导入";
    /** * eml文件后缀 */
    private static final String EML_SUFFIX = ".eml";

    /** * 解析通用邮件内容 * * @param mailItem * @param targetDir * @return */
    public static UniversalMail parseMail(MailItem mailItem, String targetDir) throws MailPlusException {
        UniversalMail universalMail = null;
        if (mailItem.getPop3Message() != null) {
            POP3Message pop3Message = mailItem.getPop3Message();
            universalMail = parseMimeMessage(pop3Message, targetDir);
            if (universalMail.getHasAttachment()) {
                List<UniversalAttachment> universalAttachments = parseAttachment(pop3Message, targetDir + "/" + universalMail.getUid());
                universalMail.setAttachments(universalAttachments);
            }
            String emlPath = saveMimiMessageAsLocalEml(pop3Message, targetDir + "/" + universalMail.getUid());
            universalMail.setEmlPath(emlPath);
        } else if (mailItem.getImapMessage() != null) {
            IMAPMessage imapMessage = mailItem.getImapMessage();
            universalMail = parseMimeMessage(imapMessage, targetDir);
            if (universalMail.getHasAttachment()) {
                List<UniversalAttachment> universalAttachments = parseAttachment(imapMessage, targetDir + "/" + universalMail.getUid());
                universalMail.setAttachments(universalAttachments);
            }
            String emlPath = saveMimiMessageAsLocalEml(imapMessage, targetDir + "/" + universalMail.getUid());
            universalMail.setEmlPath(emlPath);
        } else if (mailItem.getExchangeMessage() != null) {
            universalMail = parseExchangeMail(mailItem.getExchangeMessage());
            //解析附件
            if (universalMail.getHasAttachment()) {
                try {
                    EmailMessage exchangeMessage = mailItem.getExchangeMessage();
                    exchangeMessage.load(new PropertySet(EmailMessageSchema.MimeContent, EmailMessageSchema.AllowedResponseActions, EmailMessageSchema.Attachments));
                    List<UniversalAttachment> attachments = parseAttachment(exchangeMessage.getAttachments(), targetDir + "/" + universalMail.getUid());
                    universalMail.setAttachments(attachments);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new MailPlusException(e.getMessage());
                }
            }
            //保存到本地
            String emlPath = saveExchangeMailAsLocalEml(mailItem.getExchangeMessage(), targetDir + "/" + universalMail.getUid());
            universalMail.setEmlPath(emlPath);
        }
        return universalMail;
    }

    /** * 保存邮件到本地的eml中 * * @param mimeMessage * @param targetDir * @return */
    public static String saveMimiMessageAsLocalEml(MimeMessage mimeMessage, String targetDir) throws MailPlusException {
        try {
            String subject = mimeMessage.getSubject();
            subject = StringUtils.isEmpty(subject) ? "无主题" + System.currentTimeMillis() : subject;
            File file = new File(targetDir.concat("/")
                    .concat(mimeMessage.getSession() == null ? RandomStringUtils.random(40, true, true) : DigestUtils.md5Hex(subject))
                    .concat(EML_SUFFIX));
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            if (!file.exists()) {
                file.createNewFile();
                mimeMessage.writeTo(new FileOutputStream(file));
            }
            return file.getAbsolutePath();
        } catch (MessagingException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }

    /** * 解析邮件附件 * * @param mimeMessage * @param targetDir * @return */
    public static List<UniversalAttachment> parseAttachment(MimeMessage mimeMessage, String targetDir) throws MailPlusException {
        try {
            MimeMessageParser parser = new MimeMessageParser(mimeMessage).parse();
            List<UniversalAttachment> list = new ArrayList<>();

            File dir = new File(targetDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }

            List<DataSource> attachmentList = parser.getAttachmentList();
            Collection<String> contentIds = parser.getContentIds();
            Set<String> cidFile = new HashSet<>();
            for (String cid : contentIds) {
                DataSource attachmentByCid = parser.findAttachmentByCid(cid);
                File file = new File(targetDir + "/" + attachmentByCid.getName());
                if (!file.exists()) {
                    file.createNewFile();
                    FileUtils.copyInputStreamToFile(attachmentByCid.getInputStream(), file);
                }
                UniversalAttachment universalAttachment = UniversalAttachment.builder()
                        .cid(cid)
                        .path(file.getAbsolutePath())
                        .name(attachmentByCid.getName())
                        .contentType(attachmentByCid.getContentType())
                        .build();
                list.add(universalAttachment);
                cidFile.add(attachmentByCid.getName());
            }
            for (DataSource dataSource :
                    attachmentList) {
                if (cidFile.contains(dataSource.getName())) {
                    continue;
                }
                File file = new File(targetDir + "/" + dataSource.getName());
                if (!file.exists()) {
                    file.createNewFile();
                    FileUtils.copyInputStreamToFile(dataSource.getInputStream(), file);
                }
                UniversalAttachment universalAttachment = UniversalAttachment.builder()
                        .cid(null)
                        .path(file.getAbsolutePath())
                        .name(dataSource.getName())
                        .contentType(dataSource.getContentType())
                        .build();
                list.add(universalAttachment);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }

    /** * 解析mime邮件 * * @param mimeMessage * @param targetDir * @return * @throws MailPlusException */
    public static UniversalMail parseMimeMessage(MimeMessage mimeMessage, String targetDir) throws MailPlusException {
        try {
            MimeMessageParser parser = new MimeMessageParser(mimeMessage).parse();
            javax.mail.Folder folder = mimeMessage.getFolder();
            String uid = "";
            //组装uid
            if (mimeMessage instanceof IMAPMessage) {
                uid = folder.getName() + ((IMAPFolder) folder).getUID(mimeMessage);
            } else if (mimeMessage instanceof POP3Message) {
                uid = ((POP3Folder) folder).getUID(mimeMessage);
            } else {
                uid = UUID.randomUUID().toString();
            }
            if (StringUtils.isEmpty(uid)) {
                throw new MailPlusException("【MIME邮件解析】解析uid失败");
            }
            String subject = parser.getSubject();
            String body = parser.hasHtmlContent() ? parser.getHtmlContent() : parser.getPlainContent();
            UniversalMail universalMail = UniversalMail.builder()
                    .content(StringUtils.isEmpty(body) ? "" : EmojiParser.parseToAliases(body))
                    .uid(uid)
                    .receiver(getMimeMessageAddressJson(parser.getTo()))
                    .title(StringUtils.isEmpty(subject) ? "<无主题>" : EmojiParser.parseToAliases(subject))
                    .sendDate(mimeMessage.getSentDate())
                    .hasRead(mimeMessage.getFlags().equals(Flags.Flag.SEEN))
                    .hasAttachment(parser.hasAttachments())
                    .fromer(parser.getFrom())
                    .folder(folder != null ? folder.getName() : IMPORT_FOLDER)
                    .cc(getMimeMessageAddressJson(parser.getCc()))
                    .bcc(getMimeMessageAddressJson(parser.getBcc()))
                    .build();
            return universalMail;
        } catch (Exception e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }

    /** * 获取JSON格式的邮件地址 * * @param address * @return */
    public static String getMimeMessageAddressJson(List<Address> address) {
        List<UniversalRecipient> recipients = new ArrayList<>();
        for (int i = 0; i < address.size(); i++) {
            InternetAddress internetAddress = (InternetAddress) address.get(i);
            UniversalRecipient build = UniversalRecipient.builder()
                    .name(StringUtils.isNotEmpty(internetAddress.getPersonal()) ? EmojiParser.parseToAliases(internetAddress.getPersonal()) : internetAddress.getAddress())
                    .email(internetAddress.getAddress())
                    .build();
            recipients.add(build);
        }
        return JSON.toJSONString(recipients);
    }

    /** * 保存到本地,必须在解析完邮件之后再保存,不然会报错,加载的头不一样了 * * @param exchangeMessage * @param targetDir * @return * @throws MailPlusException */
    public static String saveExchangeMailAsLocalEml(EmailMessage exchangeMessage, String targetDir) throws MailPlusException {
        try {
            File dir = new File(targetDir);
            if (dir.exists()) {
                dir.mkdirs();
            }
            exchangeMessage.load();
            String subject = exchangeMessage.getSubject();
            subject = StringUtils.isEmpty(subject) ? "<无主题>" + System.currentTimeMillis() : subject;
            File eml = new File(targetDir.concat("/").concat(DigestUtils.md5Hex(subject).concat(EML_SUFFIX)));
            exchangeMessage.load(
                    new PropertySet(
                            EmailMessageSchema.MimeContent
                            , EmailMessageSchema.AllowedResponseActions
                    )
            );
            if (!eml.exists()) {
                File parentFile = eml.getParentFile();
                if (!parentFile.exists()) {
                    parentFile.mkdirs();
                }
                eml.createNewFile();
                byte[] content = exchangeMessage.getMimeContent().getContent();
                FileUtils.writeByteArrayToFile(eml, content);
            }
            return eml.getAbsolutePath();
        } catch (Exception e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }

    /** * 解析Exchange附件 * * @param attachments * @param targetDir * @return */
    public static List<UniversalAttachment> parseAttachment(AttachmentCollection attachments, String targetDir) {
        List<UniversalAttachment> universalAttachments = new ArrayList<>();
        List<Attachment> items = attachments.getItems();
        File dir = new File(targetDir);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        for (Attachment attachment :
                items) {
            try {
                String path = targetDir + "/" + attachment.getName();
                File file = new File(path);
                if (!file.exists()) {
                    file.createNewFile();
                    if (attachment instanceof FileAttachment) {
                        ((FileAttachment) attachment).load(file.getAbsolutePath());
                    } else if (attachment instanceof ItemAttachment) {
                        ItemAttachment itemAttachment = (ItemAttachment) attachment;
                        itemAttachment.load(ItemSchema.MimeContent);
                        Item item = itemAttachment.getItem();
                        FileUtils.writeByteArrayToFile(file, item.getMimeContent().getContent());
                    }
                }
                universalAttachments.add(
                        UniversalAttachment.builder()
                                .cid(attachment.getContentId())
                                .contentType(attachment.getContentType())
                                .name(attachment.getName())
                                .path(path)
                                .build()
                );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return universalAttachments;
    }

    /** * 解析exchange邮件 * * @param message * @return */
    public static UniversalMail parseExchangeMail(EmailMessage message) throws MailPlusException {
        try {
            message.load();
            String subject = message.getSubject();
            EmailAddress from = message.getFrom();
            String body = message.getBody().toString();
            Date dateTimeSent = null;
            try {
                dateTimeSent = message.getDateTimeSent();
            } catch (ServiceLocalException e) {
                e.printStackTrace();
            }
            UniversalMail.UniversalMailBuilder builder = UniversalMail.builder()
                    .bcc(getExchangeAddressJson(message.getBccRecipients()))
                    .cc(getExchangeAddressJson(message.getCcRecipients()))
                    .folder(Folder.bind(message.getService(), message.getParentFolderId()).getDisplayName())
                    .fromer(from == null ? "<无发件人>" : from.getAddress())
                    .hasAttachment(message.getHasAttachments())
                    .hasRead(message.getIsRead())
                    .sendDate(dateTimeSent)
                    //处理emoji
                    .title(StringUtils.isAnyEmpty(message.getSubject()) ? "<无主题>" : EmojiParser.parseToAliases(subject))
                    .receiver(getExchangeAddressJson(message.getToRecipients())).uid(message.getId().getUniqueId())
                    .content(StringUtils.isEmpty(body) ? "" : EmojiParser.parseToAliases(body));
            UniversalMail universalMail = builder.build();
            return universalMail;
        } catch (ServiceLocalException e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
            throw new MailPlusException(e.getMessage());
        }
    }

    /** * 解析exchange地址为键值对并转换成JSON字符串 * * @param recipients * @return */
    public static String getExchangeAddressJson(EmailAddressCollection recipients) {
        List<EmailAddress> items = recipients.getItems();
        List<UniversalRecipient> list = new ArrayList<>();
        for (EmailAddress emailAddress :
                items) {
            list.add(
                    UniversalRecipient.builder()
                            .email(emailAddress.getAddress())
                            .name(StringUtils.isNotEmpty(emailAddress.getName()) ? EmojiParser.parseToAliases(emailAddress.getName()) : emailAddress.getAddress())
                            .build()
            );
        }
        return JSON.toJSONString(list);
    }
}

点赞