JavaMail读取收件箱退信邮件/分析邮件附件获取Message_Id
Posted Watson-ljf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaMail读取收件箱退信邮件/分析邮件附件获取Message_Id相关的知识,希望对你有一定的参考价值。
需求描述:公司最近有个项目邮件通知功能,但是客户上传的邮件地址并不一定存在,以及其他的各种问题。所有希望发送通知后有个回执,及时发现地址存在问题的邮箱。
需求分析:经过分析JavaMail可以读取收件箱邮件,我们可以通过对应通知的退信来回写通知状态。那么问题来了,发送通知和退信如何建立映射?经过调研,最终确定采用以下方案解决。
映射方案:
- 在发送邮件通知时在Header中指定自定义的Message_Id,作为唯一标示,本系统中采用UUID。
- 定时任务扫描服务器邮箱的收件箱,本系统我们搜索收件箱中前30分钟内的主题为:“来自[email protected]的退信”,的退信邮件。
- 分析退信附件,退信关联邮件信息存在附件中,我们需要的Message_Id也在其中,解析附件获取Message_Id回写通知状态。
核心代码:
邮件搜索
1 package com.yinghuo.yingxinxin.notification.service; 2 3 import com.yinghuo.yingxinxin.notification.domain.PayrollNotificationEntity; 4 import com.yinghuo.yingxinxin.notification.domain.valobj.EmailNotificationStatus; 5 import com.yinghuo.yingxinxin.notification.repository.NotificationRepository; 6 import com.yinghuo.yingxinxin.notification.util.DateUtil; 7 import com.yinghuo.yingxinxin.notification.util.EmailUtil; 8 import com.yinghuo.yingxinxin.notification.util.StringUtil; 9 import lombok.Data; 10 import lombok.extern.slf4j.Slf4j; 11 import org.apache.commons.lang.exception.ExceptionUtils; 12 import org.springframework.boot.context.properties.ConfigurationProperties; 13 import org.springframework.stereotype.Service; 14 import org.springframework.transaction.annotation.Transactional; 15 16 import javax.mail.*; 17 import javax.mail.search.AndTerm; 18 import javax.mail.search.ComparisonTerm; 19 import javax.mail.search.SearchTerm; 20 import javax.mail.search.SentDateTerm; 21 import javax.mail.search.SubjectTerm; 22 import java.util.Arrays; 23 import java.util.Calendar; 24 import java.util.Date; 25 import java.util.Properties; 26 27 @Service 28 @Slf4j 29 @Data 30 @ConfigurationProperties(prefix = "spring.mail") 31 public class EmailBounceScanService { 32 private final static String subjectKeyword = "来自[email protected]的退信"; 33 34 private String popHost; 35 private String username; 36 private String password; 37 private Integer timeOffset; 38 private final NotificationRepository payrollSendRecordRepository; 39 40 private Properties buildInboxProperties() { 41 Properties properties = new Properties(); 42 properties.setProperty("mail.store.protocol", "pop3"); 43 properties.setProperty("mail.pop3.host", popHost); 44 properties.setProperty("mail.pop3.auth", "true"); 45 properties.setProperty("mail.pop3.default-encoding", "UTF-8"); 46 return properties; 47 } 48 49 public void searchInboxEmail() { 50 Session session = Session.getInstance(this.buildInboxProperties()); 51 Store store = null; 52 Folder receiveFolder = null; 53 try { 54 store = session.getStore("pop3"); 55 store.connect(username, password); 56 receiveFolder = store.getFolder("inbox"); 57 receiveFolder.open(Folder.READ_ONLY); 58 59 int messageCount = receiveFolder.getMessageCount(); 60 if (messageCount > 0) { 61 Date now = Calendar.getInstance().getTime(); 62 Date timeOffsetAgo = DateUtil.nextXMinute(now, timeOffset); 63 SearchTerm comparisonTermGe = new SentDateTerm(ComparisonTerm.GE, timeOffsetAgo); 64 SearchTerm search = new AndTerm(new SubjectTerm(subjectKeyword), comparisonTermGe); 65 66 Message[] messages = receiveFolder.search(search); 67 if (messages.length == 0) { 68 log.info("No bounce email was found."); 69 return; 70 } 71 this.messageHandler(messages); 72 } 73 } catch (MessagingException e) { 74 log.error("Exception in searchInboxEmail {}", ExceptionUtils.getFullStackTrace(e)); 75 e.printStackTrace(); 76 } finally { 77 try { 78 if (receiveFolder != null) { 79 receiveFolder.close(true); 80 } 81 if (store != null) { 82 store.close(); 83 } 84 } catch (MessagingException e) { 85 log.error("Exception in searchInboxEmail {}", ExceptionUtils.getFullStackTrace(e)); 86 e.printStackTrace(); 87 } 88 } 89 } 90 91 @Transactional 92 public void messageHandler(Message[] messageArray) { 93 Arrays.stream(messageArray).filter(EmailUtil::isContainAttachment).forEach((message -> { 94 String messageId = null; 95 try { 96 messageId = EmailUtil.getMessageId(message); 97 } catch (Exception e) { 98 log.error("getMessageId:", ExceptionUtils.getFullStackTrace(e)); 99 e.printStackTrace(); 100 } 101 if (StringUtil.isEmpty(messageId)) return; 102 103 PayrollNotificationEntity payrollNotificationEntity = payrollSendRecordRepository.findFirstByMessageId(messageId); 104 if (payrollNotificationEntity == null || EmailNotificationStatus.BOUNCE.getStatus() == payrollNotificationEntity.getStatus()) { 105 log.warn("not found payrollNotificationEntity by messageId:{}", messageId); 106 return; 107 } 108 109 payrollNotificationEntity.setStatus(EmailNotificationStatus.BOUNCE.getStatus()); 110 payrollNotificationEntity.setErrorMessage(EmailNotificationStatus.BOUNCE.getErrorMessage()); 111 payrollSendRecordRepository.save(payrollNotificationEntity); 112 })); 113 } 114 }
附件解析
1 package com.yinghuo.yingxinxin.notification.util; 2 3 import lombok.extern.slf4j.Slf4j; 4 5 import javax.mail.BodyPart; 6 import javax.mail.MessagingException; 7 import javax.mail.Multipart; 8 import javax.mail.Part; 9 import java.io.BufferedReader; 10 import java.io.IOException; 11 import java.io.InputStream; 12 import java.io.InputStreamReader; 13 14 @Slf4j 15 public final class EmailUtil { 16 private static final String multipart = "multipart/*"; 17 18 public static String getMessageId(Part part) throws Exception { 19 if (!part.isMimeType(multipart)) { 20 return ""; 21 } 22 23 Multipart multipart = (Multipart) part.getContent(); 24 for (int i = 0; i < multipart.getCount(); i++) { 25 BodyPart bodyPart = multipart.getBodyPart(i); 26 27 if (part.isMimeType("message/rfc822")) { 28 return getMessageId((Part) part.getContent()); 29 } 30 InputStream inputStream = bodyPart.getInputStream(); 31 32 try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) { 33 String strLine; 34 while ((strLine = br.readLine()) != null) { 35 if (strLine.startsWith("Message_Id:")) { 36 String[] split = strLine.split("Message_Id:"); 37 return split.length > 1 ? split[1].trim() : null; 38 } 39 } 40 } 41 } 42 43 return ""; 44 } 45 46 public static boolean isContainAttachment(Part part) { 47 boolean attachFlag = false; 48 try { 49 if (part.isMimeType(multipart)) { 50 Multipart mp = (Multipart) part.getContent(); 51 for (int i = 0; i < mp.getCount(); i++) { 52 BodyPart mpart = mp.getBodyPart(i); 53 String disposition = mpart.getDisposition(); 54 if ((disposition != null) && ((disposition.equals(Part.ATTACHMENT)) || (disposition.equals(Part.INLINE)))) 55 attachFlag = true; 56 else if (mpart.isMimeType(multipart)) { 57 attachFlag = isContainAttachment((Part) mpart); 58 } else { 59 String contype = mpart.getContentType(); 60 if (contype.toLowerCase().contains("application")) 61 attachFlag = true; 62 if (contype.toLowerCase().contains("name")) 63 attachFlag = true; 64 } 65 } 66 } else if (part.isMimeType("message/rfc822")) { 67 attachFlag = isContainAttachment((Part) part.getContent()); 68 } 69 } catch (MessagingException | IOException e) { 70 e.printStackTrace(); 71 } 72 return attachFlag; 73 } 74 }
以上是关于JavaMail读取收件箱退信邮件/分析邮件附件获取Message_Id的主要内容,如果未能解决你的问题,请参考以下文章
[Java] JavaMail 发送 html 格式带附件的邮件
无法使用 JavaMail 读取 Outlook 邮件,而 Gmail 可以工作