简单示例
首先需要引入需要的依赖,因为我的项目是基于Springboot做的,所以直接引入spring-mail即可:
implementation 'org.springframework.boot:spring-boot-starter-mail'
该starter中包含了java-mail的完整依赖:
创建session,需要找到邮箱服务器的pop地址、端口、是否使用ssl,找到后定义property:
private static final Properties PROPERTIES = new Properties();
PROPERTIES.put("mail.store.protocol", "pop3");
PROPERTIES.put("mail.pop3.host", HOST);
PROPERTIES.put("mail.pop3.port", "995");
// 以下是使用SSL时需要的设置
PROPERTIES.put("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
PROPERTIES.put("mail.pop3.socketFactory.fallback", "false");
Session session = Session.getDefaultInstance(PROPERTIES);
打开收件箱,Store对象实现了AutoCloseable接口,可以使用try-resource语法简化代码。 pop协议只能打开INDEX,因此代码如下:
try (Store store = session.getStore("pop3")) {
// username就是邮箱,password是邮箱密码或者授权码
store.connect(HOST, USERNAME, PASSWORD);
Folder inbox = store.getFolder("INBOX");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
遍历邮件,需要先打开文件夹,然后获取邮件列表,pop协议每次获取都是获取服务器上的全部邮件,在实际使用中需要考虑这个问题:
inbox.open(Folder.READ_ONLY);
Message[] messages = inbox.getMessages();
for (Message message : messages) {
System.out.println(message.getSubject())
}
至此获取就已经能够完成基本的邮件收取工作了。
解析邮件
主要参考java之邮件读取和解析_java读取邮件内容-CSDN博客[^1]。
需要考虑附件存储、内容读取、收件人等
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.internet.MimeUtility;
import org.eclipse.angus.mail.pop3.POP3Message;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class POP3MessageWrapper {
private final POP3Message message;
public POP3MessageWrapper(POP3Message message) {
this.message = message;
}
public String getId() throws MessagingException {
return message.getMessageID();
}
public String getSubject() throws MessagingException, UnsupportedEncodingException {
if (message.getSubject() == null) {
return "";
}
return MimeUtility.decodeText(message.getSubject());
}
public String getFrom() throws MessagingException, UnsupportedEncodingException {
StringBuilder builder = new StringBuilder();
Address[] froms = message.getFrom();
if (froms.length < 1) { throw new MessagingException("没有发件人!"); }
InternetAddress address = (InternetAddress) froms[0];
String person = address.getPersonal();
if (person != null) {
person = MimeUtility.decodeText(person) + " ";
} else {
person = "";
}
builder.append(person).append('<').append(address.getAddress()).append('>');
return builder.toString();
}
public String getReceiveAddress() throws MessagingException {
StringBuilder receiveAddress = new StringBuilder();
Address[] add = message.getAllRecipients();
if (add == null || add.length < 1) { throw new MessagingException("没有收件人!"); }
for (Address address : add) {
InternetAddress internetAddress = (InternetAddress) address;
receiveAddress.append(internetAddress.toUnicodeString()).append(",");
}
//删除最后一个逗号
receiveAddress.deleteCharAt(receiveAddress.length() - 1);
return receiveAddress.toString();
}
public Date getSentDate() throws MessagingException {
return message.getSentDate();
}
public boolean isContainAttachment() throws MessagingException, IOException {
return isContainAttachment(message);
}
public boolean isSeen() throws MessagingException {
return message.getFlags().contains(Flags.Flag.SEEN);
}
public boolean isReplySign() throws MessagingException {
boolean replySign = false;
String[] headers = message.getHeader("Disposition-Notification-To");
if (headers != null) { replySign = true; }
return replySign;
}
public String getPriority() throws MessagingException {
String priority = "普通";
String[] headers = message.getHeader("X-Priority");
if (headers != null) {
String headerPriority = headers[0];
if (headerPriority.contains("1") || headerPriority.contains("High")) { priority = "紧急"; } else if (
headerPriority.contains("5") || headerPriority.contains("Low")) { priority = "低"; } else { priority = "普通"; }
}
return priority;
}
public String getContent() throws MessagingException, IOException {
StringBuilder builder = new StringBuilder();
getMailTextContent(message, builder);
return builder.toString();
}
public List getAttachments() throws MessagingException, IOException {
return saveAttachments(message);
}
private List saveAttachments(Part part) throws MessagingException, IOException {
List result = new ArrayList<>();
if (part.isMimeType("multipart/*")) {
//复杂体邮件
Multipart multipart = (Multipart) part.getContent();
//复杂体邮件包含多个邮件体
int partCount = multipart.getCount();
for (int i = 0; i < partCount; i++) {
//获得复杂体邮件中其中一个邮件体
BodyPart bodyPart = multipart.getBodyPart(i);
//某一个邮件体也有可能是由多个邮件体组成的复杂体
String disposition = bodyPart.getDisposition();
if (disposition != null && (disposition.equalsIgnoreCase(Part.ATTACHMENT) || disposition.equalsIgnoreCase(Part.INLINE))) {
InputStream is = bodyPart.getInputStream();
result.add(saveFile(is, getSubject()+"/"+bodyPart.getFileName()));
} else if (bodyPart.isMimeType("multipart/*")) {
result.addAll(saveAttachments(bodyPart));
} else {
String contentType = bodyPart.getContentType();
if (contentType.contains("name") || contentType.contains("application")) {
result.add(saveFile(bodyPart.getInputStream(), getSubject()+"/"+bodyPart.getFileName()));
}
}
}
} else if (part.isMimeType("message/rfc822")) {
result.addAll(saveAttachments((Part) part.getContent()));
}
return result;
}
private String saveFile(InputStream inputStream, String path) {
//fixme: 完成具体的存储工作,本地存储或者nas等
return null;
}
private void getMailTextContent(Part part, StringBuilder content) throws MessagingException, IOException {
//如果是文本类型的附件,通过getContent方法可以取到文本内容,但这不是我们需要的结果,所以在这里要做判断
boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
if (part.isMimeType("text/*") && !isContainTextAttach) {
content.append(part.getContent().toString());
} else if (part.isMimeType("message/rfc822")) {
getMailTextContent((Part) part.getContent(), content);
} else if (part.isMimeType("multipart/*")) {
Multipart multipart = (Multipart) part.getContent();
int partCount = multipart.getCount();
for (int i = 0; i < partCount; i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
getMailTextContent(bodyPart, content);
}
}
}
private boolean isContainAttachment(Part part) throws MessagingException, IOException {
boolean flag = false;
if (part.isMimeType("multipart/*")) {
MimeMultipart multipart = (MimeMultipart) part.getContent();
int partCount = multipart.getCount();
for (int i = 0; i < partCount; i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
String disp = bodyPart.getDisposition();
if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
flag = true;
} else if (bodyPart.isMimeType("multipart/*")) {
flag = isContainAttachment(bodyPart);
} else {
String contentType = bodyPart.getContentType();
if (contentType.contains("application")) {
flag = true;
}
if (contentType.contains("name")) {
flag = true;
}
}
if (flag) { break; }
}
} else if (part.isMimeType("message/rfc822")) {
flag = isContainAttachment((Part) part.getContent());
}
return flag;
}
}
删除已读文件
pop协议默认是不删除服务器上的邮件的,如果确实需要删除,需要在读取邮件后,在关闭连接之前发送删除指令,大概方法如下:
try (Store store = session.getStore("pop3")) {
store.connect(HOST, USERNAME, PASSWORD);
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_ONLY);
Message[] messages = inbox.getMessages();
for (Message message : messages) {
System.out.println(message.getSubject())
// 标记删除
message.setFlag(Flags.Flag.DELETED, true);
}
// 关闭文件夹,参数为true时会删除标记删除的邮件
inbox.close(true);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
本文基本上将java-mail使用pop3收取邮件的场景覆盖,后续遇到了再补充上来吧。