zoukankan      html  css  js  c++  java
  • JavaMail读取收件箱退信邮件/分析邮件附件获取Message_Id

    需求描述:公司最近有个项目邮件通知功能,但是客户上传的邮件地址并不一定存在,以及其他的各种问题。所有希望发送通知后有个回执,及时发现地址存在问题的邮箱。

    需求分析:经过分析JavaMail可以读取收件箱邮件,我们可以通过对应通知的退信来回写通知状态。那么问题来了,发送通知和退信如何建立映射?经过调研,最终确定采用以下方案解决。

    映射方案:

    1. 在发送邮件通知时在Header中指定自定义的Message_Id,作为唯一标示,本系统中采用UUID。
    2. 定时任务扫描服务器邮箱的收件箱,本系统我们搜索收件箱中前30分钟内的主题为:“来自postmaster@net.cn的退信”,的退信邮件。
    3. 分析退信附件,退信关联邮件信息存在附件中,我们需要的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 = "来自postmaster@net.cn的退信";
     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 }
  • 相关阅读:
    [C#]mouse_event模拟点击时坐标参数无效?!
    体验boost::spirit
    喜讯,公司换宽屏液晶显示器了
    [疑难杂症]扩展AxWebBrowser的问题???
    VS 2005 BUG: 新增JScript文件编码问题引起乱码?
    在JavaScript中实现命名空间
    [C#]实现序列号生成器
    基于Web的仿WF工作流设计器
    分享:基于UDP协议实现可靠的数据传输
    远程控制之屏幕截取 小结
  • 原文地址:https://www.cnblogs.com/watson-ljf/p/9701129.html
Copyright © 2011-2022 走看看