最近在做一个邮件相关的项目,网上找资料时发现,关于发送邮件的资料倒是很多,收取邮件的就很少了。写下个人开发经验,供别人参考吧。
也可以当成工具包使用,在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方面的资料