zoukankan      html  css  js  c++  java
  • 邮件群发问题

      在做邮件发送时遇到一个问题以及解决方式,记录一下。

      我们公司的邮箱是Coremail的企业邮箱,群发邮件时发现,如果有一个邮箱地址不存在,那么本次群发会失败;而群发其他的邮箱时如qq邮箱,163邮箱如果有一个地址不存在,那么本次发送还是成功的。所以猜测可能与邮箱服务器有关。

      为了解决给北纬邮箱群发的问题,我网上查了一下,看了一下 java mail 的 api,发现发送邮件调用 send 方法时,失败会抛出异常 javax.mail.SendFailedException,而这个异常里面封装了三个变量:
    9c0c71bf00ea4f508c5250b8eb6073db.png
      invalid 是不存在的邮箱地址,validSent 是已发送的邮箱地址,validUnsent 是未发送成功的邮箱地址,因此思路就是捕获该异常,将不存在的邮箱地址去除后再次发送即可。简单的封装了一下 java mail:

      

      1 import javax.activation.DataHandler;
      2 import javax.activation.FileDataSource;
      3 import javax.mail.*;
      4 import javax.mail.internet.*;
      5 import java.io.File;
      6 import java.io.UnsupportedEncodingException;
      7 import java.util.LinkedList;
      8 import java.util.List;
      9 import java.util.Properties;
     10 
     11 /**
     12  * 邮件发送
     13  *
     14  * @author Xiaosy
     15  * @date 2017-12-28 14:26
     16  */
     17 public class EmailSender {
     18     /**
     19      * 邮件服务器地址
     20      */
     21     private String host;
     22     /**
     23      * 发件人用户名
     24      */
     25     private String username;
     26     /**
     27      * 发件人密码
     28      */
     29     private String password;
     30 
     31     private String from;
     32     /**
     33      * 邮件对象
     34      */
     35     private MimeMessage mimeMessage;
     36     /**
     37      * 接收方邮件地址
     38      */
     39     private InternetAddress[] addresses;
     40     /**
     41      * 无效的邮箱地址
     42      */
     43     private String[]invalidAddresses;
     44     /**
     45      * 已发送的邮箱
     46      */
     47     private String[]validSendAddresses;
     48     /**
     49      * 未发送成功的邮箱地址
     50      */
     51     private String[]validUnSendAddresses;
     52     private Session session;
     53     private Properties props;
     54     //附件添加的组件
     55     private Multipart mp;
     56     //附件文件
     57     private List<FileDataSource> files = new LinkedList<FileDataSource>();
     58 
     59 
     60     private EmailSender(String from, String smtpHost, String sendUserName, String sendUserPassword){
     61         this.from = from;
     62         this.host = smtpHost;
     63         this.username = sendUserName;
     64         this.password = sendUserPassword;
     65         init();
     66     }
     67     private void init(){
     68         if (props == null) {
     69             props = System.getProperties();
     70         }
     71         props.put("mail.smtp.host", this.host);
     72         props.put("mail.smtp.auth", "true"); // 需要身份验证
     73         props.put("mail.mime.splitlongparameters","false");
     74         session = Session.getDefaultInstance(props, null);
     75         // 置true可以在控制台(console)上看到发送邮件的过程
     76         session.setDebug(false);
     77         // 用session对象来创建并初始化邮件对象
     78         mimeMessage = new MimeMessage(session);
     79         // 生成附件组件的实例
     80         mp = new MimeMultipart();
     81     }
     82 
     83     public static EmailSender newInstance(String from, String smtpHost, String sendUserName, String sendUserPassword){
     84         return new EmailSender(from,smtpHost, sendUserName, sendUserPassword);
     85     }
     86 
     87     /**
     88      * 设置邮件主题
     89      * @param subject
     90      * @return
     91      * @throws MessagingException
     92      */
     93     public EmailSender subject(String subject) throws MessagingException{
     94         this.mimeMessage.setSubject(subject);
     95         return this;
     96     }
     97     public EmailSender subject(String subject, String charset) throws MessagingException{
     98         this.mimeMessage.setSubject(subject,charset);
     99         return this;
    100     }
    101 
    102     /**
    103      * 设置正文,支持html标签
    104      * @param content
    105      * @return
    106      * @throws MessagingException
    107      */
    108     public EmailSender content(String content) throws MessagingException{
    109         BodyPart bp = new MimeBodyPart();
    110         bp.setContent("<meta http-equiv=Content-Type content=text/html; charset=UTF-8>" + content, "text/html;charset=UTF-8");
    111         this.mp.addBodyPart(bp);
    112         return this;
    113     }
    114 
    115     /**
    116      * 设置收件人
    117      * @param to
    118      * @return
    119      * @throws MessagingException
    120      */
    121     public EmailSender to(String...to) throws MessagingException {
    122         this.addresses = new InternetAddress[to.length];
    123         for(int i =0;i<to.length;i++){
    124             this.addresses[i] = new InternetAddress(to[i]);
    125         }
    126         return this;
    127     }
    128     public EmailSender to(String to) throws MessagingException {
    129         this.addresses = new InternetAddress[]{new InternetAddress(to)};
    130         return this;
    131     }
    132 
    133     /**
    134      * 设置抄送
    135      * @param cc
    136      * @return
    137      * @throws MessagingException
    138      */
    139     public EmailSender cc(String...cc) throws MessagingException {
    140         InternetAddress[]addresses = new InternetAddress[cc.length];
    141         for(int i =0;i<cc.length;i++){
    142             addresses[i] = new InternetAddress(cc[i]);
    143         }
    144         this.mimeMessage.setRecipients(Message.RecipientType.CC,addresses);
    145         return this;
    146     }
    147     public EmailSender cc(String cc) throws MessagingException {
    148         InternetAddress[]addresses = new InternetAddress[]{new InternetAddress(cc)};
    149         this.mimeMessage.setRecipients(Message.RecipientType.TO,addresses);
    150         return this;
    151     }
    152 
    153     /**
    154      * 添加附件,可添加多个
    155      * @param fileName
    156      * @param file
    157      * @return
    158      * @throws MessagingException
    159      * @throws UnsupportedEncodingException
    160      */
    161     public EmailSender addAttachmentFile(String fileName, File file) throws MessagingException, UnsupportedEncodingException {
    162         BodyPart bp = new MimeBodyPart();
    163         FileDataSource fileds = new FileDataSource(file);
    164         bp.setDataHandler(new DataHandler(fileds));
    165         bp.setFileName(MimeUtility.encodeText(fileName, "utf-8", null)); // 解决附件名称乱码
    166         this.mp.addBodyPart(bp);// 添加附件
    167         this.files.add(fileds);
    168         return this;
    169     }
    170 
    171     /**
    172      * 发送邮件
    173      * @param skpiInvalidAddresses 是否跳过不正确的邮箱地址,true跳过,默认不跳过
    174      * @return
    175      */
    176     public boolean send(boolean skpiInvalidAddresses){
    177         Transport transport = null;
    178         try {
    179             this.mimeMessage.setRecipients(Message.RecipientType.TO,this.addresses);
    180             this.mimeMessage.setFrom(this.from);
    181             this.mimeMessage.setContent(this.mp);
    182             this.mimeMessage.saveChanges();
    183             System.out.println("邮件发送中...");
    184             transport = this.session.getTransport("smtp");
    185             //连接邮件服务器并进行身份验证
    186             transport.connect(this.host,this.username,this.password);
    187             //发送邮件
    188             transport.sendMessage(this.mimeMessage,this.addresses);
    189             System.out.println("发送成功");
    190             this.validSendAddresses = new String[this.addresses.length];
    191             for(int i=0;i<this.addresses.length;i++){
    192                 this.validSendAddresses[i] = this.addresses[i].toString();
    193             }
    194         }catch (SendFailedException e){
    195             Address[]invalid = e.getInvalidAddresses();
    196             if(invalid != null && invalid.length > 0){
    197                 this.invalidAddresses = new String[invalid.length];
    198                 for(int i=0;i<invalid.length;i++){
    199                     this.invalidAddresses[i]=invalid[i].toString();
    200                 }
    201             }
    202             Address[]validSendTo = e.getValidSentAddresses();
    203             if(validSendTo != null && validSendTo.length > 0){
    204                 this.validSendAddresses = new String[validSendTo.length];
    205                 for(int i=0;i<validSendTo.length;i++){
    206                     this.validSendAddresses[i]=validSendTo[i].toString();
    207                 }
    208             }
    209             Address[]validUnSendTo = e.getValidUnsentAddresses();
    210             if(validUnSendTo != null && validUnSendTo.length > 0){
    211                 this.validUnSendAddresses = new String[validUnSendTo.length];
    212                 for(int i=0;i<validUnSendTo.length;i++){
    213                     this.validUnSendAddresses[i]=validUnSendTo[i].toString();
    214                 }
    215             }
    216             //跳过不正确的邮箱
    217             if(skpiInvalidAddresses && invalid != null && invalid.length>0){
    218                 if(validUnSendTo != null && validUnSendTo.length > 0){
    219                     this.validUnSendAddresses = null;
    220                     return sendFailedAddresses(validUnSendTo);
    221                 }
    222             }else {
    223                 return false;
    224             }
    225 
    226         } catch (MessagingException e) {
    227             e.printStackTrace();
    228             return false;
    229         }finally {
    230             if(transport != null){
    231                 try {
    232                     transport.close();
    233                 } catch (MessagingException e) {
    234                     e.printStackTrace();
    235                 }
    236             }
    237         }
    238         return true;
    239     }
    240 
    241     public boolean send(){
    242         return send(false);
    243     }
    244 
    245     private boolean sendFailedAddresses(Address[]failedAddresses){
    246         Transport transport = null;
    247         try {
    248             this.mimeMessage.setRecipients(Message.RecipientType.TO,failedAddresses);
    249             transport = this.session.getTransport("smtp");
    250             //连接邮件服务器并进行身份验证
    251             transport.connect(this.host,this.username,this.password);
    252             //发送邮件
    253             transport.sendMessage(this.mimeMessage,failedAddresses);
    254             this.validSendAddresses = new String[failedAddresses.length];
    255             for(int i=0;i<failedAddresses.length;i++){
    256                 this.validSendAddresses[i] = failedAddresses[i].toString();
    257             }
    258         }catch (SendFailedException e){
    259             e.printStackTrace();
    260             return false;
    261         }catch (MessagingException e){
    262             e.printStackTrace();
    263             return false;
    264         }
    265         return true;
    266     }
    267 
    268 
    269     public String[] getInvalidAddresses() {
    270         return invalidAddresses;
    271     }
    272 
    273     public void setInvalidAddresses(String[] invalidAddresses) {
    274         this.invalidAddresses = invalidAddresses;
    275     }
    276 
    277     public String[] getValidSendAddresses() {
    278         return validSendAddresses;
    279     }
    280 
    281     public void setValidSendAddresses(String[] validSendAddresses) {
    282         this.validSendAddresses = validSendAddresses;
    283     }
    284 
    285     public String[] getValidUnSendAddresses() {
    286         return validUnSendAddresses;
    287     }
    288 
    289     public void setValidUnSendAddresses(String[] validUnSendAddresses) {
    290         this.validUnSendAddresses = validUnSendAddresses;
    291     }
    292 }

      测试代码如下:

      

     1  @Test
     2 public void testSendMail(){
     3     String subject = "测试用";
     4     String content = "测试内容";
     5     String[]emails = new String[]{"myqq@qq.com","aaa@abc.com","bbb@bw.com"};
     6 
     7     try {
     8         EmailSender javaMailSender = EmailSender.newInstance(from,mailConfiguration.getHost(),mailConfiguration.getUsername(),mailConfiguration.getPassword())
     9                     .subject(subject).content(content).to(emails);
    10         System.out.println(javaMailSender.send());
    11         String[]invalidAddress = javaMailSender.getInvalidAddresses();
    12         System.out.println("Invalid address : " + JSONObject.toJSONString(invalidAddress));
    13         String[]validUnSentAddress = javaMailSender.getValidUnSendAddresses();
    14         System.out.println("valid unsent address : " + JSONObject.toJSONString(validUnSentAddress));
    15         String[]validSentAddress = javaMailSender.getValidSendAddresses();
    16         System.out.println("valid sent address : " + JSONObject.toJSONString(validSentAddress));
    17     } catch (MessagingException e) {
    18         e.printStackTrace();
    19     }
    20 }

    其中,send() 方法默认是不跳过不存在的邮箱地址的,如果要自动跳过,参数设置为 true 即可:
    默认时的结果如下:
    4bb20654a59b41f392d789ed33b26314.png

    参数设置为 true 时结果如下:
    6634e315f07c465ebd901129f51a9678.png

    而我的 qq 邮箱也确实收到了邮件。

    如果使用 spring 封装的 mail,调用 mailSender.send(MimeMessage message) 方法时抛出的异常是 org.springframework.mail.MailSendException,查看源码发现这个异常并没有 ivali dAddress、validSentAddress 和 validUnSentAddress 三个变量,但是它有一个异常数组:
    2ec43987a24e420cb993f6760ceff111.png
    javax.mail.SendFailedException 应该被放到了这里面,修改 spring 发送邮件的代码如下:

     1 /**
     2  * 发送邮件
     3  * @param skpiInvalidAddresses 是否跳过不正确的邮箱地址,true跳过,默认不跳过
     4  * @return
     5  */
     6 public boolean send(boolean skpiInvalidAddresses) throws IllegalStateException{
     7     if(mailSender == null){
     8         LOGGER.info("Mail Sender Instance is not allowed null");
     9         throw new IllegalStateException("You should call newInstance() method first before call send() method");
    10     }
    11     try {
    12         mailSender.send(this.message);
    13     }catch (MailSendException e){
    14         Exception[] messageExceptions = e.getMessageExceptions();
    15         if(messageExceptions != null && messageExceptions.length > 0) {
    16             SendFailedException subEx = null;
    17             for (int i = 0; i < messageExceptions.length; ++i) {
    18                 if (messageExceptions[i] instanceof SendFailedException) {
    19                     subEx = (SendFailedException) messageExceptions[i];
    20                     break;
    21                 }
    22             }
    23             if (subEx == null) {
    24                 return false;
    25             }
    26             Address[] invalid = subEx.getInvalidAddresses();
    27             if (invalid != null) {
    28                 this.invalidAddresses = new String[invalid.length];
    29                 for (int j = 0; j < invalid.length; j++) {
    30                     this.invalidAddresses[j] = invalid[j].toString();
    31                 }
    32             }
    33 
    34             Address[] validUnsentAddresses = subEx.getValidUnsentAddresses();
    35             if (validUnsentAddresses != null) {
    36                 this.validUnSendAddresses = new String[validUnsentAddresses.length];
    37                 for (int j = 0; j < validUnsentAddresses.length; j++) {
    38                     this.validUnSendAddresses[j] = validUnsentAddresses[j].toString();
    39                 }
    40             }
    41 
    42             Address[]validSentAddresses = subEx.getValidSentAddresses();
    43             if(validSentAddresses != null){
    44                 this.validSendAddresses = new String[validSentAddresses.length];
    45                 for(int j=0;j<validSentAddresses.length;j++){
    46                     this.validSendAddresses[j] = validSentAddresses[j].toString();
    47                 }
    48             }
    49 
    50             if(skpiInvalidAddresses){
    51                 return sendFailedAddress(this.validUnSendAddresses);
    52             }else {
    53                 return false;
    54             }
    55         }
    56     }
    57     return true;
    58 }

    经过测试同样可以正常工作。

    需要注意的是,发送邮件前必须设置 From 字段,如果未设置此字段,invalidAddress 是获取不到地址错误的邮箱的,而导致群发的所有邮箱地址放到了 validUnSentAddress 里面,这样就跟通常的情况是一样的了。

    小提示:邮件发送 html 的内容时,如果有图片时,使用图片链接显示的话,很多邮箱服务器会屏蔽外部链接导致图片显示出现问题,可以采用内嵌方式将图片内嵌入正文中,但是这种方式会增加邮件大小。

  • 相关阅读:
    guzzle 中间件原理
    K8S-K8S 环境搭建
    K8S-k8s 理念知识
    云计算的概念
    Linux-DHCP 交互的过程
    linux-怎么踢出系统当前已连接的用户
    linux-Centos 搭建http yum源
    linux-硬链接与软连接
    linux-centos网络配置bond
    linux-dd 一个测试文件
  • 原文地址:https://www.cnblogs.com/xiaosiyuan/p/8617540.html
Copyright © 2011-2022 走看看