import java.util.Properties;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import java.io.File;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.SendFailedException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.sun.mail.smtp.SMTPMessage;
import com.sun.mail.util.MailSSLSocketFactory;
import red.sea.commons.util.BeanTool;
import red.sea.mail.data.MailEntity;
import red.sea.mail.data.OaMailAccount;
import red.sea.mail.data.OaMailResource;
import red.sea.mail.service.IOaMailOutlookService;
/***
* 邮件发送工具类
* 1.读取账户中配置的邮箱账户信息
* 2.根据邮件队列中的数据进行搜索轮询。
* 3.根据轮询结果进行邮件的发送,发送失败则进行失败提示,并保存到数据库。
*使用生产消费类:
*MailSendQueueTool.mapPool.put("邮件resourceId唯一", new MailEntity(m,s,l));
*
*/
public class MailSendQueueTool implements ServletContextListener {
/**
* 缓存对象 map
*/
// public static CachePool<String, Object> mapPool = CachePool.getInstance();
//线程数
// private static final int NTHREADS=5;
// 使用线程池来避免 为每个请求创建一个线程。
// private static final Executor threadPool=Executors.newFixedThreadPool(NTHREADS);
/***
* 邮件发送服务改造。
* 0.设置监听定时器邮件定时发送邮件。
* 1.改造为异步邮件发送服务。
* 2.使用多线程和队列进行邮箱发送。
* @param mailAccount
* @param mailresource
* @param attachment
* @return
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听器启动......");
new MailSendDaemon().startTread();
System.out.println("监听器结束.............");
/*Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
//启动邮件发送线程。
System.out.println("启动邮件定时发送服务,queue剩余"+mapPool.size()+"条");
if(mapPool.size()!=0){
startThread();
}else{
System.out.println("队列中数量0无邮件可发.........");
}
System.out.println("关闭邮件定时服务:"+new Date());
}},1000*60,20000); //1分钟之后发邮件,每隔5秒钟发送一次邮件。
*/
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------
package red.sea.email.common;
import java.text.DateFormat;
import java.util.Properties;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.AuthenticationFailedException;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.NoSuchProviderException;
import javax.mail.SendFailedException;
import javax.mail.internet.AddressException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import javax.mail.util.ByteArrayDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.sun.mail.smtp.SMTPMessage;
import com.sun.mail.util.MailSSLSocketFactory;
import red.sea.commons.util.BeanTool;
import red.sea.email.data.MailAccount;
import red.sea.email.data.MailList;
import red.sea.email.service.IMailListService;
/***
* 邮件发送工具类
* 1.读取账户中配置的邮箱账户信息
* 2.根据邮件队列中的数据进行搜索轮询。
* 3.根据轮询结果进行邮件的发送,发送失败则进行失败提示,并保存到数据库。
*
*/
public class MailSendTool {
private static Log log = LogFactory.getLog(MailSendTool.class);
//设置线程池
private static Executor executor = Executors.newFixedThreadPool(10);
private static String protocol2 = "smtp";
/***
* 邮件发送服务改造。
* 1.改造为异步邮件发送服务。
* 2.使用多线程和队列进行邮箱发送。
* @param mailAccount
* @param mailresource
* @param attachment
* @return
*/
public int sendMail( MailAccount mailAccount,
MailList mailresource, List attachment) {
//是否启用ssl发送邮件
Properties properties = new Properties();
properties.setProperty("mail.smtp.host", mailAccount.getSmtpServer());
properties.put("mail.smtp.auth", "true"); // 允许smtp校验
//如果启用ssl邮箱发送服务
if(mailAccount.getIsSendSsl().equals("1")){
properties.setProperty("mail.smtp.port", mailAccount.getSendPort());
MailSSLSocketFactory sf = null;
try {
sf = new MailSSLSocketFactory();
} catch (GeneralSecurityException e) {
// logger.fatal("error", e);
}
sf.setTrustAllHosts(true);
properties.put("mail.smtp.ssl.enable", "true");
//关键
//properties.put("mail.smtp.starttls.enable","true");
properties.put("mail.smtp.ssl.socketFactory", sf);
properties.put("mail.smtp.socketFactory.fallback", "false");
properties.put("mail.smtp.socketFactory.port", mailAccount.getSendPort());
}
Session session = Session.getInstance(properties, null);
Transport transport = null;
SMTPMessage message = null;
int flag = -1;
try {
// 验证发件服务器
try{
if(transport==null){
flag = 4;
transport = session.getTransport("smtp");
transport.connect(mailAccount.getSmtpServer(), mailAccount.getEmail(),mailAccount.getPwd());
}
}catch (Exception e) {
System.out.println("连接出错:"+e.getMessage());
}
flag = 5;
message = new SMTPMessage(session);
// 为true时,则即使地址数组addresses中有错误的地址,邮件也可以发送给其余正确的地址
message.setSendPartial(true);
//在这儿可以指定发送人姓名的编码方式,默认为GBK
message.setFrom(new InternetAddress(mailAccount.getEmail(),
mailAccount.getName(),"UTF-8"));
if (!mailresource.getMRev().equalsIgnoreCase("")) {
String[] addresses = mailresource.getMRev().split(";");
for (int i = 0; i < addresses.length; i++) {
message.addRecipients(Message.RecipientType.TO,
addresses[i]);
}
}
//抄送
if (!mailresource.getMCc().equalsIgnoreCase("")) {
String[] addresses = mailresource.getMCc().split(";");
for (int i = 0; i < addresses.length; i++) {
message.addRecipients(Message.RecipientType.CC,
addresses[i]);
}
}
//密送
if (!mailresource.getMBcc().equalsIgnoreCase("")) {
String[] addresses = mailresource.getMBcc().split(";");
for (int i = 0; i < addresses.length; i++) {
message.addRecipients(Message.RecipientType.BCC,
addresses[i]);
}
}
//在这儿可以指定邮件主题的编码方式,默认为GBK
message.setSubject(mailresource.getMTitle(),"GBK");
// 设置发送级别
message.addHeader("X-Priority", "3");
// 设置是否回执
if (mailresource.getIsReturnReceipt().equalsIgnoreCase("1")) {
message.addHeader("Disposition-Notification-To", mailAccount
.getEmail());
}
// 向multipart对象中添加邮件的各个部分内容,包括文本内容和附件
Multipart multipart = new MimeMultipart();
// 添加邮件正文
BodyPart contentPart = new MimeBodyPart();
contentPart.setContent(mailresource.getMContent(), "text/html;charset=UTF-8");
multipart.addBodyPart(contentPart);
// 添加附件的内容
if (attachment != null) {
//多附件上传
//File[] f=attachment;
for(int i=0;i<attachment.size();i++){
File uploadFile=(File) attachment.get(i);
BodyPart attachmentBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(uploadFile);
attachmentBodyPart.setDataHandler(new DataHandler(source));
// 解决文件名乱码的方法,其实用MimeUtility.encodeWord就可以很方便的搞定
// 这里很重要,通过下面的Base64编码的转换可以保证你的中文附件标题名在发送时不会变成乱码
//sun.misc.BASE64Encoder enc = new sun.misc.BASE64Encoder();
//attachmentBodyPart.setFileName("=?GBK?B?" + enc.encode(uploadFile.getName().getBytes()) + "?=");
//MimeUtility.encodeWord可以避免文件名乱码
attachmentBodyPart.setFileName(MimeUtility.encodeText(uploadFile.getName()));
multipart.addBodyPart(attachmentBodyPart);
// multipart.addBodyPart(attachmentBodyPart);
}
}
// 将multipart对象放到message中
message.setContent(multipart);
// Set the content
message.setSentDate(new Date());
// 保存邮件
message.saveChanges();
// Send message
transport.sendMessage(message, message.getAllRecipients());
//return 0;// 成功
//发送成功修改数据状态为已发送。
System.out.println("发送成功。。");
} catch (NoSuchProviderException e) {
//log.error("邮箱校验错误:", e);
e.printStackTrace();
//return 1;
} catch (AuthenticationFailedException e) {
//log.error("邮箱校验错误:", e);
e.printStackTrace();
//return 2;
} catch (SendFailedException e) {
Address[] address = e.getInvalidAddresses();
if(address!=null){
int invalidAddress = e.getInvalidAddresses().length;
if(invalidAddress > 0){ // 有错误地址导致发送失败
sendFailMail(mailAccount, message, e);
//发送失败后,设置数据库result表发送失败。
/*for(Address feiledMail:address){
//修改发送状态
// modifySendState( mailAccount, mailresource,feiledMail,"邮箱不存在,发送失败","0");
}*/
}
}
//log.error("邮箱校验错误:", e);
e.printStackTrace();
return 3;
} catch (MessagingException e) {// 其他邮件错误
//log.error("邮箱校验错误:", e);
e.printStackTrace();
//return flag;
} catch (Exception e) {
//log.error("邮箱校验错误:", e);
e.printStackTrace();
return 6;
} finally {
if (transport != null) {
try {
transport.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
return 0;
}
/***
* 邮件发送失败修改发送状态
* @param mailAccount
* @param mailresource
* @param feiledMail
*/
private void modifySendState(MailAccount mailAccount,
MailList mailresource, Address feiledMail,String info,String state) {
IMailListService mailListService =(IMailListService) BeanTool.getBean( "mailListService" );
Map<Object,Object> map=new HashMap<Object, Object>();
//邮件id
map.put("resourceID", mailresource.getMId());
//邮件接收者
map.put("receiveMailId", feiledMail);
//邮件发送状态,0失败;1成功
map.put("state", state);
//提示内容
map.put("info", info);
//修改邮箱状态
//mailListService.updateStateByResourceId(map);
}
/**
* 将列表中的字符串转换成InternetAddress对象
* @param list 邮件字符串地址列表
* @return InternetAddress对象数组
*/
private InternetAddress[] InternetAddresses(Object[] list) {
if (list == null || list.length<0) {
return null;
}
int size = list.length;
InternetAddress[] arr = new InternetAddress[size];
for (int i = 0; i < size; i++) {
try {
arr[i] = new InternetAddress(list[i].toString());
} catch (AddressException e) {
e.printStackTrace();
}
}
return arr;
}
// 发送失败后,给用户发送提示邮件
public static void sendFailMail(MailAccount mailAccount, Message message, String folderTitle, String info){
Transport transport = null;
String userName = mailAccount.getName();
String sendTo = mailAccount.getEmail();
try {
// -----------------提示邮件内容------开始-------------------
StringBuffer sb = new StringBuffer("");
sb.append("您从").append(folderTitle).append("发送的邮件:<br><br>");
if(message!=null){
sb.append(getOriginalMessageContent(message, userName));
} else {
sb.append("没有发送成功<br>");
}
sb.append("发送失败的原因是:").append(info).append("<br><br>");
// -----------------提示邮件内容------结束-------------------
//mailAccount superAccount = new QuerySuperAccountMail();
Properties properties = new Properties();
properties.put("mail.smtp.host", mailAccount.getSendPort());
properties.put("mail.smtp.auth", "true");
Session session = Session.getInstance(properties, null);
transport = session.getTransport(protocol2);
transport.connect(null, mailAccount.getEmail(),
mailAccount.getPwd());
MimeMessage newMessage = new MimeMessage(session);
String sendFrom = mailAccount.getEmail();
if(sendFrom==null){
sendFrom = "";
}
getNewMessage(sb, message, newMessage, sendFrom, sendTo);
// Send newMessage
transport.sendMessage(newMessage, newMessage.getAllRecipients());
} catch (NoSuchProviderException e1) {
log.error("邮箱校验错误:", e1);
} catch (AuthenticationFailedException e1) {
log.error("邮箱校验错误:", e1);
} catch (SendFailedException e1) {
log.error("邮箱校验错误:", e1);
} catch (MessagingException e1) { // 其他邮件错误
log.error("邮箱校验错误:", e1);
} catch (Exception e1) {
log.error("邮箱校验错误:", e1);
} finally {
if (transport != null) {
try {
transport.close();
} catch (Exception e1) {
}
}
}
}
// 群发时,有错误地址导致发送失败,调用此方法给用户发送一封提示邮件
private static void sendFailMail(MailAccount mailAccount,
Message message, SendFailedException e) {
int invalidAddressNum = 0;
int validSentAddressNum = 0;
int validUnsentAddressNum = 0;
if(e.getInvalidAddresses()!=null){
invalidAddressNum = e.getInvalidAddresses().length;
}
if(e.getValidSentAddresses()!=null){
validSentAddressNum = e.getValidSentAddresses().length;
}
if(e.getValidUnsentAddresses()!=null){
validUnsentAddressNum = e.getValidUnsentAddresses().length;
}
String userName = mailAccount.getName();
String sendTo = mailAccount.getEmail();
Transport transport = null;
try {
// -----------------提示邮件内容------开始-------------------
StringBuffer sb = new StringBuffer("您发送的邮件:<br><br>");
sb.append(getOriginalMessageContent(message, userName));
sb.append("发送失败的原因是:某些收件人或抄送人地址有错误<br><br>");
sb.append("其中: <br>");
sb.append("错误的地址数为:").append(invalidAddressNum).append("<br>");
if(invalidAddressNum > 0){
sb.append("错误地址如下:").append(getAddresses(e.getInvalidAddresses())).append("<br>");
}
sb.append("正确且送达的地址数为:").append(validSentAddressNum).append("<br>");
sb.append("正确但未送达的地址数为:").append(validUnsentAddressNum).append("<br>");
if(validUnsentAddressNum > 0){ // 有未送达的正确地址
sb.append("正确但未送达的地址如下: ").append(getAddresses(e.getValidUnsentAddresses())).append("<br>");
}
// -----------------提示邮件内容------结束-------------------
//QuerySuperAccountMail superAccount = new QuerySuperAccountMail();
Properties properties = new Properties();
properties.put("mail.smtp.host", mailAccount.getSendPort());
properties.put("mail.smtp.auth", "true");
Session session = Session.getInstance(properties, null);
transport = session.getTransport(protocol2);
transport.connect(null, mailAccount.getEmail(),
mailAccount.getEmail());
MimeMessage newMessage = new MimeMessage(session);
String sendFrom = mailAccount.getEmail();
if(sendFrom==null){
sendFrom = "";
}
getNewMessage(sb, message, newMessage, sendFrom, sendTo);
// Send newMessage
transport.sendMessage(newMessage, newMessage.getAllRecipients());
} catch (NoSuchProviderException e1) {
log.error("邮箱校验错误:", e1);
} catch (AuthenticationFailedException e1) {
log.error("邮箱校验错误:", e1);
} catch (SendFailedException e1) {
log.error("邮箱校验错误:", e1);
} catch (MessagingException e1) { // 其他邮件错误
log.error("邮箱校验错误:", e1);
} catch (Exception e1) {
log.error("邮箱校验错误:", e1);
} finally {
if (transport != null) {
try {
transport.close();
} catch (Exception e1) {
}
}
}
}
private static String getOriginalMessageContent(Message message,
String userName) throws MessagingException {
StringBuffer sb = new StringBuffer("------------Original Message------------<br>");
sb.append(" 发件人: ").append(userName).append("<br>");
Address[] sendToAddr = message.getRecipients(Message.RecipientType.TO);
Address[] sendCcAddr = message.getRecipients(Message.RecipientType.CC);
if (sendToAddr != null) {
// 如果地址不多,则列出收件人
if (sendToAddr.length < 20) {
sb.append(" 收件人: ").append(getAddresses(sendToAddr)).append("<br>");
} else {
sb.append(" 收件人: ").append("收件人较多,此处略").append("<br>");
}
}
if (sendCcAddr != null) {
// 如果地址不多,则列出抄送人
if (sendCcAddr.length < 20) {
sb.append(" 抄送人: ").append(getAddresses(sendCcAddr)).append("<br>");
} else {
sb.append(" 抄送人: ").append("抄送人较多,此处略").append("<br>");
}
}
sb.append(" 主题: ").append(message.getSubject()).append("<br>");
sb.append(" 发送时间: ").append(parseDate(message)).append("<br>");
sb.append("------------Original Message------------<br><br>");
sb.append("没有发送成功!原邮件副本见附件<br>");
return sb.toString();
}
// 从传入的Address数组里,取出所有地址
private static String getAddresses(Address[] addresses){
if(addresses==null){
return "";
}
int len = addresses.length;
StringBuffer sb = new StringBuffer("");
for(int i=0; i<len; i++){
InternetAddress addr = (InternetAddress)addresses[i];
sb.append(addr.getAddress()).append(";");
}
return sb.toString();
}
private static String parseDate(Message message) throws MessagingException{
String sendTime = "";
Date date = message.getSentDate();
if(date!=null){
// 格式化日期时间
sendTime = DateFormat.getDateTimeInstance().format(date);
}
if (sendTime.equals("")) {
// 取得邮件生成日期时间
String[] createDateArr = message.getHeader("Date");
if (createDateArr != null && createDateArr.length > 0) {
sendTime = createDateArr[0];
}
}
return sendTime;
}
private static void getNewMessage(StringBuffer sb, Message message, MimeMessage newMessage, String sendFrom, String sendTo)
throws MessagingException, IOException {
// 在这儿可以指定发送人姓名的编码方式,默认为GBK
newMessage.setFrom(new InternetAddress(sendFrom, "系统管理员", "GBK"));
if (!sendTo.equalsIgnoreCase("")) {
newMessage.addRecipients(Message.RecipientType.TO, sendTo);
}
// 在这儿可以指定邮件主题的编码方式,默认为GBK
if(message==null){
newMessage.setSubject("未送达:原邮件不存在", "GBK");
} else {
newMessage.setSubject("未送达:" + message.getSubject(), "GBK");
}
// 优先级设为普通级
newMessage.addHeader("X-Priority", "3");
Multipart mp = new MimeMultipart();
// 提示邮件分两部分,第一部分是正文内容,第二部分是附件(把原邮件作为附件)
int bodyPartLength = 2;
if(message==null){
// 原邮件不存在,无法作为附件,所以提示邮件bodyPatr长度为1
bodyPartLength = 1;
}
MimeBodyPart amimebodypart[] = new MimeBodyPart[bodyPartLength];
for (int j = 0; j < bodyPartLength; j++)
amimebodypart[j] = new MimeBodyPart();
amimebodypart[0].setDataHandler(new DataHandler(
new ByteArrayDataSource(sb.toString(),
"text/html; charset=GBK")));
mp.addBodyPart(amimebodypart[0]);
if(message!=null){
ByteArrayOutputStream bos = new ByteArrayOutputStream();
message.writeTo(bos);
amimebodypart[1].setDataHandler(new DataHandler(
new ByteArrayDataSource(bos.toByteArray(), "message/rfc822; charset=GBK")));
amimebodypart[1].setFileName(MimeUtility.encodeWord(message.getSubject()+".eml"));
mp.addBodyPart(amimebodypart[1]);
}
// Set the content
newMessage.setContent(mp);
newMessage.setSentDate(new Date());
newMessage.saveChanges();
}
public static void main(String[] args) {
/*OaMailAccount occ=new OaMailAccount();
occ.setSmtpServer("smtp.qq.com");
occ.setMailUserId("2364106@qq.com");
occ.setMailPassword("kmfiacrytthdbjfb");
occ.setMailAddress("2364106@qq.com");
occ.setMailUserName("奥迪中国");
occ.setIsSendSsl("1");
occ.setSendPort("587");
occ.setSmtpServer("121.8.125.74");
occ.setMailUserId("hrtest888@redsea888.com");
occ.setMailPassword("hrtest666");
occ.setMailAddress("hrtest888@redsea888.com");
occ.setMailUserName("朱锦全");
occ.setIsSendSsl("1");
occ.setSendPort("25");
OaMailResource cr=new OaMailResource();
cr.setResTo("hrtest888@hr-soft.cn");
cr.setResCc("");
cr.setResBcc("");
cr.setResTitle("发ssl邮件并且带附件");
cr.setResContent("发ssl邮件并且带附件内容");
cr.setIsReturnReceipt("0");
File file=new File("D:\\E\\JEECMS简介.docx");
List<File> fileList=new ArrayList<File>();
//fileList.add(file);
System.out.println("开始发送。");
//发送
sendMail(occ, cr, fileList);
System.out.println("发送完成。");*/
MailAccount occ=new MailAccount();
occ.setSmtpServer("smtp.163.com");
occ.setEmail("18026214961@163.com");
occ.setPwd("123abc");
occ.setName("奥迪中国");
occ.setIsSendSsl("1");
occ.setSendPort("587");
MailList cr=new MailList();
cr.setMRev("zhujq201191@126.com");
cr.setMCc("");
cr.setMBcc("");
cr.setMTitle("main方法测试002");
cr.setMContent("main方法测试002");
cr.setIsReturnReceipt("0");
//File file=new File("E:\\mail\\3a劳务派遣企业等级评价申请表.doc");
List<File> fileList=new ArrayList<File>();
//fileList.add(file);
MailSendTool sendTool=new MailSendTool();
//发送
sendTool.sendMail( occ, cr, fileList);
//sendMail();
System.out.println("发送完成。");
}
}