package
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestMailSender {
private static final Logger log = LoggerFactory.getLogger(MailSender.class);
/* 邮箱配置详情 */
private static final String MAIL_SMTP_AUTH = "true";
private static final String MAIL_HOST = "";
private static final String MAIL_TRANSPORT_PROTOCOL = "smtp";
private static final String MAIL_SMTP_PORT = "25";
private static final String MAIL_AUTH_NAME = "";
private static final String MAIL_AUTH_PASSWORD = "";
private static final String MAIL_DISPLAY_SENDNAME = "发件人";
private static final String MAIL_DISPLAY_SENDMAIL = "";
private static final String MAIL_SEND_CHARSET = "UTF-8";
private static final boolean MAIL_IS_DEBUG = true;
/* 邮箱逻辑配置 */
private static final int MAIL_FAILED_RESEND_TIMES = 3; // 同步、异步发送邮件失败,尝试发送次数
private static final long MAIL_FAILED_ASYN_RESEND_WAITING_MILLISECONDS = 10000; // 异步多线程发送邮件失败,重试间隔时间
private static final long MAIL_FAILED_SYNC_RESEND_WAITING_MILLISECONDS = 3000; // 同步发送邮件失败,线程等待时间
public static final long MAIL_ASYN_SEND_SPACING_MILLISECONDS = 2000;
public static final long MAIL_SYNC_SEND_SPACING_MILLISECONDS = 1000;
private static final Message message = initMessage();
// 初始化邮箱配置
private static final Message initMessage() {
// 基本配置
Properties props = new Properties();
props.setProperty("mail.smtp.auth", MAIL_SMTP_AUTH);
props.setProperty("mail.host", MAIL_HOST);
props.setProperty("mail.transport.protocol", MAIL_TRANSPORT_PROTOCOL);
props.setProperty("mail.smtp.port", MAIL_SMTP_PORT);
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(MAIL_AUTH_NAME,
MAIL_AUTH_PASSWORD);
}
});
session.setDebug(MAIL_IS_DEBUG);
Message message = new MimeMessage(session);
try {
message.setFrom(new InternetAddress(MimeUtility
.encodeText(MAIL_DISPLAY_SENDNAME)
+ "<"
+ MAIL_DISPLAY_SENDMAIL + ">"));
} catch (AddressException e) {
log.error("初始化邮箱配置失败", e);
throw new RuntimeException(e.getMessage());
} catch (UnsupportedEncodingException e) {
log.error("初始化邮箱配置失败", e);
throw new RuntimeException(e.getMessage());
} catch (MessagingException e) {
log.error("初始化邮箱配置失败", e);
throw new RuntimeException(e.getMessage());
}
return message;
}
/**
* 异步根据主题和内容发送邮件
*
* @param subject 邮件主题
* @param content 邮件内容
* @param beSentMails 接收邮件的邮箱地址,不合法的邮箱会自动过滤,如果没有合法邮箱不会发送任何邮件
*/
public static void asynMail(String subject, String content, String... beSentMails) {
Thread thread = new MailSenderByContentDetailThread(subject, content, beSentMails);
thread.start();
}
/**
* 异步根据主题和内容发送邮件(带附件)
*
* @param subject 邮件主题
* @param content 邮件内容
* @param attachmentName 附件文件名
* @param attachmentFile 附件地址
* @param beSentMails 接收邮件的邮箱地址,不合法的邮箱会自动过滤,如果没有合法邮箱不会发送任何邮件
*/
public static void asynMultiMail(String subject, String content, String attachmentName, String attachmentFile, String... beSentMails) {
Thread thread = new MailSenderByContentDetailThread(subject, content, attachmentName, attachmentFile,beSentMails);
thread.start();
}
/**
* 内部包装,根据主题和内容发送邮件
*
* @param subject 邮件主题
* @param content 邮件内容
* @param beSentMails 接收邮件的邮箱地址,不合法的邮箱会自动过滤,如果没有合法邮箱不会发送任何邮件
*/
private static final synchronized void sendMail(String subject, String content, String... beSentMails) {
// 过滤不合法邮箱
beSentMails = validBeSentMails(beSentMails);
// 检验过滤结果
if (ArrayUtils.isEmpty(beSentMails)) {
return;
}
try {
message.setSubject(subject);
message.setContent(content,"text/html;charset=" + MAIL_SEND_CHARSET );
Address[] addresses = new Address[beSentMails.length];
for (int i = 0; i < beSentMails.length; i++) {
addresses[i] = new InternetAddress(beSentMails[i]);
}
message.setRecipients(Message.RecipientType.TO, addresses);
Transport.send(message);
} catch (Exception e) {
log.error("根据主题和内容发送邮失败", e);
throw new RuntimeException("resend" + e.getMessage());
}
}
/**
* 内部包装,根据主题和内容发送邮件(带附件)
*
* @param subject 邮件主题
* @param content 邮件内容
* @param attachmentName 附件文件名
* @param attachmentFile 附件地址
* @param beSentMails 接收邮件的邮箱地址,不合法的邮箱会自动过滤,如果没有合法邮箱不会发送任何邮件
*/
private static final synchronized void sendMultiMail(String subject, String content, String attachmentName, String attachmentFile, String... beSentMails) {
// 过滤不合法邮箱
beSentMails = validBeSentMails(beSentMails);
// 检验过滤结果
if (ArrayUtils.isEmpty(beSentMails)) {
return;
}
try {
//邮件标题
message.setSubject(subject);
//message.setContent(content,"text/html;charset=" + MAIL_SEND_CHARSET );
Multipart multipart = new MimeMultipart();
//设置邮件的文本内容
BodyPart contentPart = new MimeBodyPart();
contentPart.setText(content);
multipart.addBodyPart(contentPart);
//添加附件
BodyPart messageBodyPart= new MimeBodyPart();
DataSource source = new FileDataSource(attachmentFile);
//添加附件的内容
messageBodyPart.setDataHandler(new DataHandler(source));
//添加附件的标题
messageBodyPart.setFileName(attachmentName);
multipart.addBodyPart(messageBodyPart);
//将multipart对象放到message中
message.setContent(multipart);
//保存邮件
message.saveChanges();
Address[] addresses = new Address[beSentMails.length];
for (int i = 0; i < beSentMails.length; i++) {
addresses[i] = new InternetAddress(beSentMails[i]);
}
message.setRecipients(Message.RecipientType.TO, addresses);
Transport.send(message);
} catch (Exception e) {
log.error("根据主题和内容发送邮失败", e);
throw new RuntimeException("resend" + e.getMessage());
}
}
// 返回所有合法的邮箱,都不合法则返回空
private static String[] validBeSentMails(String... beSentMails) {
// 没有任何接收邮箱
if (ArrayUtils.isEmpty(beSentMails)) {
return null;
}
// 验证邮箱格式合法性
Pattern pattern = Pattern.compile("^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$");
List<String> finalBeSentMails = new ArrayList<String>();
for (String tmp : beSentMails) {
if (StringUtils.isEmpty(tmp)) {
continue;
}
Matcher matcher = pattern.matcher(tmp);
if (matcher.matches()) {
finalBeSentMails.add(tmp);
}
}
// 只给合法的地址发送邮件
if (finalBeSentMails.size() != beSentMails.length) {
if (finalBeSentMails.size() == 0) {
return null;
}
return finalBeSentMails.toArray(new String[finalBeSentMails.size()]);
}
// 所有邮箱均合法
return beSentMails;
}
// 根据详情发送邮件线程类
static final class MailSenderByContentDetailThread extends Thread {
private String subject;
private String content;
private String[] beSentMails;
private String attachmentName;//附件文件名
private String attachmentFile;//附件文件位置
MailSenderByContentDetailThread(String subject, String content, String[] beSentMails) {
this.subject = subject;
this.content = content;
this.beSentMails = beSentMails;
this.attachmentName = null;
this.attachmentFile = null;
}
MailSenderByContentDetailThread(String subject, String content, String attachmentName, String attachmentFile, String[] beSentMails) {
this.subject = subject;
this.content = content;
this.beSentMails = beSentMails;
this.attachmentName = attachmentName;
this.attachmentFile = attachmentFile;
}
@Override
public void run() {
int sendTimes = 0;
int i = 0;
// 发送次数在允许尝试范围内
do {
try {
// 发送邮件
for (; i < beSentMails.length; i++) {
if(StringUtils.isNotBlank(attachmentName) && StringUtils.isNotBlank(attachmentFile)){
TestMailSender.sendMultiMail(subject, content, attachmentName, attachmentFile, beSentMails[i]);
}else{
TestMailSender.sendMail(subject, content, beSentMails[i]);
}
if (i < beSentMails.length - 1) {
Thread.sleep(MAIL_ASYN_SEND_SPACING_MILLISECONDS);
}
}
break;
} catch(Exception e) {
// 不是邮件发送的异常,直接输出错误信息并跳出
if (!e.getMessage().startsWith("resend")) {
log.error("邮件发送失败", e);
break;
}
// 发送次数已到达允许尝试范围,记录错误信息
if (sendTimes == MAIL_FAILED_RESEND_TIMES) {
log.error("邮件发送失败,错误原因:{}" , e.getMessage().substring(6, e.getMessage().length() - 1));
log.error("发送主题为:{}" ,subject);
log.error("发送内容为:{}" , content);
log.error("接收邮箱有:{}" , Arrays.toString(beSentMails));
log.error("为<{}>发送邮件时出错,后续邮件均未发送。",beSentMails[i]);
break;
}
// 线程休眠后重试
try {
Thread.sleep(MAIL_FAILED_ASYN_RESEND_WAITING_MILLISECONDS);
} catch (InterruptedException e1) {
log.error("线程休眠后重试失败", e1);
}
}
} while (sendTimes++ < MAIL_FAILED_RESEND_TIMES);
}
}
}