手记

POP3、IMAP4和Exchange收取邮件的JAVA开发详解(一)——工具包的整体结构和业务接口

最近在做一个邮件相关的项目,网上找资料时发现,关于发送邮件的资料倒是很多,收取邮件的就很少了。写下个人开发经验,供别人参考吧。

也可以当成工具包使用,在pom文件中添加

<repositories>
    <repository>
        <id>jitpack.io</id>
        <url>https://jitpack.io</url>
    </repository>
</repositories>
<dependency>
    <groupId>com.github.zeemood</groupId>
    <artifactId>mail-plus</artifactId>
    <version>1.0.3</version>
</dependency>

1. 开发准备

两个邮箱帐号:一个网易的,一个微软的,个人邮箱即可。例如163,hotmail。网易的邮箱主要是用来测试POP3和IMAP4的,hotmail则是用来测试Exchange的。网易邮箱需要在账户设置开通POP3和IMAP4的功能,开通方法如下:

网易使用IMAP的时候,还需要额外开通一次,不然没办法收件,收取邮件会被阻止,提示NO Select Unsafe Login. Please contact kefu@188.com for help。
开通方法是用http://config.mail.163.com/settings/imap/index.jsp?uid=xxxxxx@163.com

把上面链接的uid替换成自己的网易邮箱即可。

2.邮件收取的基本流程

创建邮箱服务器的连接 >> 列举邮箱的文件夹 >> 匹对需要同步的邮件(根据邮件UID进行匹配)>> 解析邮件

根据这个流程,可以抽取成一个接口类IMailService。在抽取之前我们先来了解一下,拉取邮件需要一些什么参数。

  • 收取邮件需要使用到的依赖
    由于我们需要收取的是POP3、IMAP4和Exchange三种不同协议的邮件,会使用到两种不同的邮箱依赖,java mail和Exchange的java依赖,解析邮件的时候额外还需要使用到apache多个依赖和Emoji表情处理的依赖。主要的依赖如下:
<!--exchange邮件服务-->
<dependency>
    <groupId>com.microsoft.ews-java-api</groupId>
    <artifactId>ews-java-api</artifactId>
    <version>2.0</version>
</dependency>

<!--邮件解析-->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-email</artifactId>
    <version>1.5</version>
</dependency>

<!--emoji文字转换-->
<dependency>
    <groupId>com.vdurmont</groupId>
    <artifactId>emoji-java</artifactId>
    <version>4.0.0</version>
</dependency>

<!--pop3和imap4的邮件服务-->
<dependency>
   <groupId>com.sun.mail</groupId>
   <artifactId>javax.mail</artifactId>
   <version>1.6.2</version>
   <scope>compile</scope>
 </dependency>
 
<!--json工具-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>
  • 邮箱服务需要的基本参数
    无疑 帐号,密码 是必须的。需要提醒一下的是IMAP、POP3的密码是自己设置的,和登录密码一点关系都没有。当然也有其他邮箱服务商开通IMAP和POP3的时候,直接把你登录密码作为协议密码的,如新浪和21cn的邮箱。而Exchange是一个特例,直接就是使用登录密码,也不需要做什么配置。
    然后是服务器地址端口,这个是IMAP和POP3需要的参数,Exchange有提供自动填入服务器地址和端口的函数。
    我们把它抽取成一个配置类MailConnCfg,代码如下:
/**
 * 邮箱登录配置
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MailConnCfg {

    /**
     * 邮箱
     */
    private String email;
    /**
     * 密码
     */
    private String password;
    /**
     * 服务器地址
     */
    private String host;
    /**
     * 端口号
     */
    private Integer port;
    /**
     * 是否使用加密方式传输
     */
    private boolean ssl;

    /**
     * 代理类型
     */
    private ProxyTypeEnum proxyType;
    /**
     * HTTP代理地址
     */
    private String proxyHost;
    /**
     * HTTP代理端口
     */
    private Integer proxyPort;
    /**
     * HTTP代理用户名
     */
    private String proxyUsername;
    /**
     * HTTP代理密码
     */
    private String proxyPassword;
    /**
     * Socks代理端口
     */
    private String socksProxyHost;
    /**
     * socks代理端口
     */
    private Integer socksProxyPort;

    public MailConnCfg(String email, String password, String host, Integer port) {
        this.email = email;
        this.password = password;
        this.host = host;
        this.port = port;
    }
}

其中的代理配置类型枚举类代码ProxyTypeEnum如下所示

/**
 * 代理类型
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@Getter
@AllArgsConstructor
public enum ProxyTypeEnum {

    /**
     * 代理类型
     */
    SOCKS, HTTP;

}
  • 邮箱服务器连接实体类
    因为同时使用了java-mail和微软的ews的依赖,所以创建出来的网络连接实体类并不是一脉相承的父子类,对两个依赖的网络连接实体类进行一下包装(当然,你有更好的方法也可以使用,我能想到的就是用泛型,还有下面这种,我觉得对我的项目方便一点
/**
 * 邮件服务器连接
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MailConn {

    private POP3Store pop3Store;
    private IMAPStore imapStore;
    private ExchangeService exchangeService;
}
  • 通用的邮件列表项
    每个装有邮件文件夹打开后,都有邮件(废话),但是三种协议的邮件列表项都不一样。打个比方说,IMAPMessage的uid(邮件唯一标识)是纯数字,只针对那个账户和文件夹唯一。而POP3Message的uid则是字符串,不同的帐号邮件id也是唯一的。Exchange就更不用说了,ews和java-mail的封装肯定不一样。但是他们都能被转换成EML文件。所以我们创建一个通用的邮件列表项,来保证接口类。代码如下:
/**
 * 通用邮件列表项
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class MailItem {
    private IMAPMessage imapMessage;
    private POP3Message pop3Message;
    private EmailMessage exchangeMessage;
}
  • 通用邮件解析载体
    解析后的邮件数据可以用一个自定义的类来承载,代码如下
/**
 * 通用邮件实体类
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UniversalMail {

    private String uid;
    private String fromer;
    private String receiver;
    private String bcc;
    private String cc;
    private String folder;
    private Boolean hasRead;
    private Boolean hasAttachment;
    private Date sendDate;
    private String title;
    private String emlPath;
    private String content;
    private String email;
    private List<UniversalAttachment> attachments;
}
/**
 * 通用邮件附件类
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class UniversalAttachment {
    private String path;
    private String cid;
    private String name;
    private String contentType;
}
/**
 * 通用邮件收件人对象
 * 
 * @author zeemoo
 * @date 2019/01/17
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UniversalRecipient {
    private String name;
    private String email;
}
  • 邮件服务接口类
    IMailService的代码如下所示:
/**
 * 邮件收取开发业务接口
 *
 * @author zeemoo
 * @date 2019/01/17
 */
public interface IMailService {

    /**
     * 解析邮件
     *
     * @param mailItem
     * @param mailItem
     * @return
     * @throws MailPlusException 自定义异常
     */
    UniversalMail parseEmail(MailItem mailItem, String localSavePath) throws MailPlusException;

    /**
     * 列举需要被同步的邮件
     *
     * @param mailConn
     * @param existUids 已拉取的邮件uid
     * @return
     * @throws MailPlusException 自定义异常
     */
    List<MailItem> listAll(MailConn mailConn, List<String> existUids) throws MailPlusException;

    /**
     * 连接服务器
     *
     * @param mailConnCfg 连接配置
     * @param proxy       是否代理
     * @return 返回连接
     */
    MailConn createConn(MailConnCfg mailConnCfg, boolean proxy) throws MailPlusException;
}
  • 自定义的异常
    主要是为了统一 一下抛出的邮件异常类型,方便开发,实际显示的异常信息还是原来的信息
/**
 * 邮箱开发包自定义异常
 *
 * @author zeemoo
 * @date 2019/01/17
 */
@NoArgsConstructor
public class MailPlusException extends Exception {
    private static final long serialVersionUID = -8014572218613182580L;

    public MailPlusException(String message) {
        super(message);
    }
}

下一篇讲下接口三个实现类。

特别提示:对于上面的@Data之类的注解是使用了Lombok依赖,用来生成getter/setter方法。Builder生成Builder方法,不懂可以找下lombok方面的资料

8人推荐
随时随地看视频
慕课网APP