最近项目实现了一个使用javaMail来实现邮箱邮件的读取和邮件回复。本人查阅资料并汇总做个记录。
需要提前了解邮件协议
- 收取邮箱邮件的协议主要有两个,第一个是pop3,第二个是imap协议。两者协议唯一区别就是pop3不能识别邮箱邮件是否为已读需要自己去判断,而imap就很轻松做到这一点。我在这里选择imap协议
- 发送协议选择smtp协议
常见邮箱类型的协议
我们项目不能确定客户到底使用什么类型的邮箱,所以自己单独测试了各类型的邮箱做个汇总,省的再去翻阅资料查询。注意:以下邮箱密码如果是授权码,需要单独查询各类型邮箱获取授权码的方式!如果是密码则直接使用邮箱密码即可!
QQ邮箱
imap服务器:imap.qq.com
smtp服务器:smtp.qq.com
smtp端口:587
用户名:账号名称
密码:授权码
企业邮箱
imap服务器:imap.exmail.qq.com
smtp服务器:smtp.exmail.qq.com
smtp端口:25
用户名:账号名称
密码:密码
新浪邮箱
imap服务器:imap.sina.com
smtp服务器:smtp.sina.com
smtp端口:25
用户名:账号名称
密码:授权码
outlook邮箱
imap服务器:outlook.office365.com
smtp服务器:smtp-mail.outlook.com
smtp端口:587
用户名:账号名称
密码:密码
163邮箱
imap服务器:imap.163.com
smtp服务器:smtp.163.com
smtp端口:25
用户名:账号名称
密码:授权码
139邮箱
139邮箱:
imap服务器:imap.139.com
smtp服务器:smtp.139.com
smtp端口:25
** 阿里云邮箱 **
阿里云邮箱:
imap服务器:imap.aliyun.com
smtp服务器:smtp.aliyun.com
smtp端口:25
用户名:账号名称
密码:密码
接下来不多BB,直接上代码(代码里的带前后~~ ~~格式的需要自己去填写具体的协议、账号、密码 )
- 收取邮件
/**
1. 读取邮件
*/
public void readEmail() {
Properties props = new Properties();
props.put("mail.imap.host", "~~imapHost~~ ");
props.put("mail.imap.auth", "true");
props.setProperty("mail.store.protocol", "imap");
props.put("mail.imap.starttls.enable", "true");
Session session = Session.getInstance(props);
try {
Store store = session.getStore();
store.connect("~~imapHost~~ ","~~userName~~ ", "~~passWord~~ ");
Folder folder = store.getFolder("Inbox");
//javamail中使用id命令有校验checkOpened, 所以要去掉id方法中的checkOpened();(这里针对163邮箱无法收发票做的逻辑代码)
IMAPFolder imapFolder = (IMAPFolder)folder;
imapFolder.doCommand(p -> { p.id("FUTONG");
return null;
});
//邮箱打开方式
folder.open(Folder.READ_WRITE);
//收取未读邮件
Message[] messages = folder.getMessages(folder.getMessageCount() - folder.getUnreadMessageCount() + 1, folder.getMessageCount());
//解析邮件
if (messages.length != 0) {
parseMessage(messages);
}
//设置邮件为已读
folder.setFlags(messages, new Flags(Flags.Flag.SEEN), true);
folder.close(true);
store.close();
} catch (MessagingException e) {
e.printStackTrace();
}
}
/**
* 解析邮件
*/
public void parseMessage(Message[] messages) {
for (Message message : messages) {
try {
//获取未读邮件
if (!message.getFlags().contains(Flags.Flag.SEEN)) {
MimeMessage mimeMsg = (MimeMessage) message;
//判断邮件是否包含附件
boolean containAttachment = this.containAttachment(mimeMsg);
// 解析邮件发件人
EmailInfo emailInfo = this.processEmailInfo(mimeMsg);
log.debug("-----> 发件人信息:{}, 包含附件:{}", emailInfo, containAttachment);
if (containAttachment) {
// 邮箱附件解析
List<BodyPart> mimeFileList = this.getMimeFile(mimeMsg);
// 附件操作(遍历可以拿到每个文件的名称和流)
saveEmailAttach(mimeFileList);
}
}
} catch (MessagingException e) {
e.printStackTrace();
}
}
}
/**
* 邮件是否包含附件
*/
public static boolean containAttachment(MimeMessage mimeMsg) {
try {
if (mimeMsg.isMimeType(MULTIPART_MIME_TYPE)) {
Multipart mp = (Multipart) mimeMsg.getContent();
for (int i = 0; i < mp.getCount(); i++) {
BodyPart bp = mp.getBodyPart(i);
// 如果该BodyPart对象包含附件
if (bp.getDisposition() != null) {
return true;
}
}
}
} catch (MessagingException | IOException e) {
log.error("-----> {}", LogUtil.stackTraceInfo(e));
}
return false;
}
/**
* 解析邮件发件信息
*/
public static EmailInfo processEmailInfo(MimeMessage mimeMsg) {
EmailInfo emailInfo = new EmailInfo();
try {
String from = ((InternetAddress) (mimeMsg.getFrom()[0])).getAddress();
String subject = mimeMsg.getSubject();
Date sentDate = mimeMsg.getSentDate();
emailInfo.setFrom(from)
.setSubject(subject)
.setSendDate(sentDate)
.setSendDateStr(DateUtil.format(sentDate, DatePattern.NORM_DATETIME_PATTERN));
} catch (MessagingException e) {
log.error("-----> {}", LogUtil.stackTraceInfo(e));
}
return emailInfo;
}
/**
* 解析邮件附件
*
* @param part 邮件中多个组合体中的其中一个组合体
*/
public static List<BodyPart> getMimeFile(Part part) {
List<BodyPart> files = new ArrayList<>();
try {
if (part.isMimeType(MULTIPART_MIME_TYPE)) {
//复杂体邮件
Multipart multipart = (Multipart) 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))) {
files.add(bodyPart);
} else if (bodyPart.isMimeType(MULTIPART_MIME_TYPE)) {
getFile(bodyPart);
} else {
String contentType = bodyPart.getContentType();
if (contentType.contains("name") || contentType.contains("application")) {
files.add(bodyPart);
}
}
}
} else if (part.isMimeType(RFC822_MIME_TYPE)) {
getMimeFile((BodyPart) part.getContent());
}
} catch (IOException | MessagingException e) {
log.error("-----> {}", LogUtil.stackTraceInfo(e));
}
return files;
}
/**
* 保存附件
*/
public void saveEmailAttach(List<BodyPart> bodyPartList) {
ArrayList<Attach> attachList = new ArrayList<>();
try {
for (BodyPart file : bodyPartList) {
String fileNameSuffix = file.getFileName();
fileNameSuffix = MimeUtility.decodeText(fileNameSuffix);
String suffix = fileNameSuffix.substring(fileNameSuffix.lastIndexOf(".") + 1);
suffix = MimeUtility.decodeText(suffix);
//判断附件类型是否为PDF(看具体情况具体处理)
if (!suffix.equalsIgnoreCase("PDF")) {
continue;
}
//获取输入流
InputStream inputStream = file.getInputStream();
//TODO
}
} catch (MessagingException | IOException e) {
throw new CommonException(e);
}
}
/**
* 发件人信息
*/
public class EmailInfo {
/** 发件人 */
private String from;
/** 主题 */
private String subject;
/** 发件时间 */
private Date sendDate;
/** 发件时间字符串 */
private String sendDateStr;
}
2. 回复邮件
/**
* @param smtpHost 邮箱服务器地址
* @param smtpPort 邮箱服务器端口
* @param userName 邮箱用户名
* @param password 邮箱密码
* @param sendAddr 发送地址(多个收件人以逗号分割)
* @param subject 邮件主题
* @param message 邮件内容
* @param attr_path 附件(文件地址)
*/
private void sendMsg(String smtpHost, String smtpPort, String userName, String password, String sendAddr, String subject, String message, String attr_path) {
try {
Properties props = new Properties();
// 开启debug调试
props.setProperty(EmailConstant.MAIL_DEBUG, EmailConstant.FALSE);
// 发送服务器需要身份验证
props.setProperty(EmailConstant.MAIL_SMTP_AUTH, EmailConstant.TRUE);
// 设置邮件服务器主机名
log.info("----->回复邮件邮箱的服务器配置" + smtpHost);
props.setProperty(EmailConstant.MAIL_HOST, smtpHost);
// 发送邮件协议名称
props.setProperty(EmailConstant.MAIL_TRANSPORT_PROTOCOL, EmailConstant.SMTP);
log.info("----->回复邮件的服务器端口配置" + smtpPort);
props.setProperty(EmailConstant.MAIL_SMTP_PORT, smtpPort);
props.put(EmailConstant.MAIL_SMTP_STARTTLS_ENABLE, EmailConstant.TRUE);
props.put(EmailConstant.MAIL_SMTP_SSL_TRUST, smtpHost);
props.put(EmailConstant.MAIL_SMTP_CONNECT_TIMEOUT, EmailConstant.MAIL_IMAP_TIMEOUT_VALUE);
props.put(EmailConstant.MAIL_SMTP_TIMEOUT, EmailConstant.MAIL_IMAP_TIMEOUT_VALUE);
props.put(EmailConstant.MAIL_SMTP_WRITE_TIMEOUT, EmailConstant.MAIL_IMAP_TIMEOUT_VALUE);
// 设置环境信息
Session session = Session.getInstance(props);
// 创建邮件对象
MimeMessage msg = new MimeMessage(session);
// 设置发件人
msg.setFrom(new InternetAddress(userName));
// 设置收件人
String[] result = sendAddr.split(",");
InternetAddress[] sendTo = new InternetAddress[result.length];
for (int i = 0; i < result.length; i++) {
log.info("----->需发送到邮箱{}:" + result[i]);
sendTo[i] = new InternetAddress(result[i]);
}
msg.addRecipients(Message.RecipientType.TO, sendTo);
// 设置邮件主题
msg.setSubject(subject);
//新建一个存放信件内容的BodyPart对象
BodyPart mdp = new MimeBodyPart();
//给BodyPart对象设置内容和格式/编码方式
mdp.setContent(message, EmailConstant.MESSAGE);
//新建一个MimeMultipart对象用来存放BodyPart对象(事实上可以存放多个)
Multipart mm = new MimeMultipart();
//将BodyPart加入到MimeMultipart对象中(可以加入多个BodyPart)
mm.addBodyPart(mdp);
if (StrUtil.isNotEmpty(attr_path)) {
DataSource attr_ds = new FileDataSource(new File(attr_path));
DataHandler attr_handler = new DataHandler(attr_ds);
BodyPart attachmentBodyPart = new MimeBodyPart();
attachmentBodyPart.setDataHandler(attr_handler);
attachmentBodyPart.setFileName(MimeUtility.encodeWord(attr_path.substring(attr_path.lastIndexOf("/") + 1)));
mm.addBodyPart(attachmentBodyPart);
}
//把mm作为消息对象的内容
msg.setContent(mm);
Transport transport = session.getTransport();
// 连接邮件服务器
log.info("----->连接收票邮箱" + userName);
transport.connect(userName, password);
// 发送邮件
log.info("----->发送回复邮件");
transport.sendMessage(msg, sendTo);
// 关闭连接
transport.close();
} catch (Exception e) {
log.error("----->回复邮件异常{}", LogUtil.stackTraceInfo(e));
}
}
public class EmailConstant {
public static String MAIL_IMAP_HOST = "mail.imap.host";
public static String MAIL_IMAP_AUTH = "mail.imap.auth";
public static String MAIL_STORE_PROTOCOL = "mail.store.protocol";
public static String MAIL_IMAP_STARTTLS_ENABLE = "mail.imap.starttls.enable";
public static String MAIL_IMAP_CONNECT_TIMEOUT = "mail.imap.connectiontimeout";
public static String MAIL_IMAP_TIMEOUT = "mail.imap.timeout";
public static String MAIL_IMAP_WRITE_TIMEOUT = "mail.imap.writetimeout";
public static String MAIL_SMTP_CONNECT_TIMEOUT = "mail.smtp.connectiontimeout";
public static String MAIL_SMTP_TIMEOUT = "mail.smtp.timeout";
public static String MAIL_SMTP_WRITE_TIMEOUT = "mail.smtp.writetimeout";
public static String MAIL_DEBUG = "mail.debug";
public static String MAIL_SMTP_AUTH = "mail.smtp.auth";
public static String MAIL_HOST = "mail.host";
public static String MAIL_TRANSPORT_PROTOCOL = "mail.transport.protocol";
public static String MAIL_SMTP_PORT = "mail.smtp.port";
public static String MAIL_SMTP_STARTTLS_ENABLE = "mail.smtp.starttls.enable";
public static String MAIL_SMTP_SSL_TRUST = "mail.smtp.ssl.trust";
public static String MESSAGE = "text/html;charset=UTF-8";
public static int MAIL_IMAP_TIMEOUT_VALUE = 5000;
public static String MAIL_SPECIAL = "@163.com";
public static String MAIL_IMAP_SSL_TRUST = "mail.imap.ssl.trust";
public static String MAIL_IMAP_SSL_SOCKETFACTORY = "mail.imap.ssl.socketFactory";
public static String IMAP = "imap";
public static String SMTP = "smtp";
public static String TRUE = "true";
public static String FALSE = "false";
public static String EMAIL_PROCESS = "emailProcess";
public static String INVOICE_CODE = "invoiceCode";
public static String INVOICE_NUMBER = "invoiceNumber";
}
补充(导包贴图)
**全部代码都在这里,我在里面删除了自己好多项目具体自己处理的逻辑,有不对的地方可以指出,我再进行修改。**