【通知】
【通知】充值首选支付宝,未及时到账,请邮件联系 youjianqunfa@qq.com 备注用户名。
当前位置:首页 > 邮箱群发百科 > 正文内容
7月12日

时间:2020-07-12 17:43

邮件群发问题

0
分类:邮箱群发百科 | 评论:0人 | 浏览:71次   

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

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

  为了解决给北纬邮箱群发的问题,我网上查了一下,看了一下 java mail 的 api,发现发送邮件调用 send 方法时,失败会抛出异常 javax.mail.SendFailedException,而这个异常里面封装了三个变量:
邮件群发问题
  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 即可:
默认时的结果如下:
邮件群发问题

参数设置为 true 时结果如下:
邮件群发问题

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

 

如果使用 spring 封装的 mail,调用 mailSender.send(MimeMessage message) 方法时抛出的异常是 org.springframework.mail.MailSendException,查看源码发现这个异常并没有 ivali dAddress、validSentAddress 和 validUnSentAddress 三个变量,但是它有一个异常数组:
邮件群发问题
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 的内容时,如果有图片时,使用图片链接显示的话,很多邮箱服务器会屏蔽外部链接导致图片显示出现问题,可以采用内嵌方式将图片内嵌入正文中,但是这种方式会增加邮件大小。

  • 评论:(0)
  • 有图有真相

已有 0 位网友发表了一针见血的评论,你还等什么?

你必须 登录 才可以留言.