共计 2936 个字符,预计需要花费 8 分钟才能阅读完成。
发送 Email 的过程我们在上一节已经讲过了,客户端总是通过 SMTP 协议把邮件发送给 MTA。
接收 Email 则相反,因为邮件最终到达收件人的 MDA 服务器,所以,接收邮件是收件人用自己的客户端把邮件从 MDA 服务器上抓取到本地的过程。
接收邮件使用最广泛的协议是 POP3:Post Office Protocol version 3,它也是一个建立在 TCP 连接之上的协议。POP3 服务器的标准端口是110
,如果整个会话需要加密,那么使用加密端口995
。
另一种接收邮件的协议是 IMAP:Internet Mail Access Protocol,它使用标准端口 143
和加密端口993
。IMAP 和 POP3 的主要区别是,IMAP 协议在本地的所有操作都会自动同步到服务器上,并且,IMAP 可以允许用户在邮件服务器的收件箱中创建文件夹。
JavaMail 也提供了 IMAP 协议的支持。因为 POP3 和 IMAP 的使用方式非常类似,这里我们只介绍 POP3 的用法。
使用 POP3 收取 Email 时,我们无需关心 POP3 协议底层,因为 JavaMail 提供了高层接口。首先需要连接到 Store 对象:
// 准备登录信息: | |
String host = "pop3.example.com"; | |
int port = 995; | |
String username = "[email protected]"; | |
String password = "password"; | |
Properties props = new Properties(); | |
props.setProperty("mail.store.protocol", "pop3"); // 协议名称 | |
props.setProperty("mail.pop3.host", host);// POP3 主机名 | |
props.setProperty("mail.pop3.port", String.valueOf(port)); // 端口号 | |
// 启动 SSL: | |
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); | |
props.put("mail.smtp.socketFactory.port", String.valueOf(port)); | |
// 连接到 Store: | |
URLName url = new URLName("pop3", host, post, "", username, password); | |
Session session = Session.getInstance(props, null); | |
session.setDebug(true); // 显示调试信息 | |
Store store = new POP3SSLStore(session, url); | |
store.connect(); |
一个 Store
对象表示整个邮箱的存储,要收取邮件,我们需要通过 Store
访问指定的 Folder
(文件夹),通常是INBOX
表示收件箱:
// 获取收件箱: | |
Folder folder = store.getFolder("INBOX"); | |
// 以读写方式打开: | |
folder.open(Folder.READ_WRITE); | |
// 打印邮件总数 / 新邮件数量 / 未读数量 / 已删除数量: | |
System.out.println("Total messages:" + folder.getMessageCount()); | |
System.out.println("New messages:" + folder.getNewMessageCount()); | |
System.out.println("Unread messages:" + folder.getUnreadMessageCount()); | |
System.out.println("Deleted messages:" + folder.getDeletedMessageCount()); | |
// 获取每一封邮件: | |
Message[] messages = folder.getMessages(); | |
for (Message message : messages) {// 打印每一封邮件: | |
printMessage((MimeMessage) message); | |
} |
当我们获取到一个 Message
对象时,可以强制转型为 MimeMessage,然后打印出邮件主题、发件人、收件人等信息:
void printMessage(MimeMessage msg) throws IOException, MessagingException {// 邮件主题: | |
System.out.println("Subject:" + MimeUtility.decodeText(msg.getSubject())); | |
// 发件人: | |
Address[] froms = msg.getFrom(); | |
InternetAddress address = (InternetAddress) froms[0]; | |
String personal = address.getPersonal(); | |
String from = personal == null ? address.getAddress() : (MimeUtility.decodeText(personal) + "<" + address.getAddress() + ">"); | |
System.out.println("From:" + from); | |
// 继续打印收件人: | |
... | |
} |
比较麻烦的是获取邮件的正文。一个 MimeMessage
对象也是一个 Part
对象,它可能只包含一个文本,也可能是一个 Multipart
对象,即由几个 Part
构成,因此,需要递归地解析出完整的正文:
String getBody(Part part) throws MessagingException, IOException {if (part.isMimeType("text/*")) {// Part 是文本: | |
return part.getContent().toString(); | |
} | |
if (part.isMimeType("multipart/*")) {// Part 是一个 Multipart 对象: | |
Multipart multipart = (Multipart) part.getContent(); | |
// 循环解析每个子 Part: | |
for (int i = 0; i < multipart.getCount(); i++) {BodyPart bodyPart = multipart.getBodyPart(i); | |
String body = getBody(bodyPart); | |
if (!body.isEmpty()) {return body; | |
} | |
} | |
} | |
return ""; | |
} |
最后记得关闭 Folder
和Store
:
folder.close(true); // 传入 true 表示删除操作会同步到服务器上(即删除服务器收件箱的邮件) | |
store.close(); |
练习
使用 POP3 接收邮件。
下载练习
小结
使用 Java 接收 Email 时,可以用 POP3 协议或 IMAP 协议。
使用 POP3 协议时,需要用 Maven 引入 JavaMail 依赖,并确定 POP3 服务器的域名/端口/是否使用 SSL 等,然后,调用相关 API 接收 Email。
设置 debug 模式可以查看通信详细内容,便于排查错误。
