接受邮件
# 接受邮件
发送 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 的使用方式非常类似,我们简单介绍下 IMAP。
收取 Email 时,我们无需关心 POP3/IMAP 的协议底层,因为 JavaMail 提供了高层接口。
我们创建一个 mail2.properties:密码已脱敏
mail.imap.host=imap.qq.com
mail.imap.port=993
mail.imap.socketFactory.class=javax.net.ssl.SSLSocketFactory
mail.imap.socketFactory.port=993
mail.imap.username=peterjxl@qq.com
mail.imap.password=***
2
3
4
5
6
创建 Store 对象,并连接:
Properties properties = new Properties();
properties.load(new FileInputStream("src/chapter20/mail2.properties"));
String host = properties.getProperty("mail.imap.host");
String username = properties.getProperty("mail.imap.username");
String password = properties.getProperty("mail.imap.password");
URLName url = new URLName("imap", host, 993, "", username, password);
Session session = Session.getInstance(properties, null);
session.setDebug(true);
Store store = new IMAPStore(session, url);
store.connect(); // 连接服务器,不连接则会报错 jakarta.mail.MessagingException: Not connected
2
3
4
5
6
7
8
9
10
11
一个 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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
当我们获取到一个 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);
// 继续打印其他信息,例如收件人,正文.........
}
2
3
4
5
6
7
8
9
10
11
比较麻烦的是获取邮件的正文。一个 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 "";
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
最后记得关闭 Folder
和 Store
:
folder.close(true); // 传入true表示删除操作会同步到服务器上(即删除服务器收件箱的邮件)
store.close();
2
# 小结
使用 Java 接收 Email 时,可以用 POP3 协议或 IMAP 协议。
设置 debug 模式可以查看通信详细内容,便于排查错误。