**发送邮件实现的代码和配置文件的加载,统一将邮件相关的配置置于配置文件中,增强工具类的适应性。
闲来无事,自己写了一个工具类,发送邮件使用自定义的模板,配置文件采用了 xml 和 properties 文件,并且采用 JSSE 的 SSL socketfactory 来取代默认的 socketfactory。**
相关 jar 包:
*①:commons-lang-2.4.jar
②:jdom.jar
③:mail.jar
*整个的参数处理,配置文件加载,详见代码即其中的注释:
邮件模板:xxx.model**
[lqs-##] [lqs-##],此为注释 --- 此为实例模板,图片 logo 替换规则 cid:logo , 正文替换格式${xxx},在调用的时候自定义替换其中的内容 2017年3月28日18:27:07
<div id="mainmail" style="margin-bottom: 12px; position: relative; z-index: 1;">
<div class="body" id="contentDiv" style="padding: 15px 15px 10px; height: auto; line-height: 1.7; font-size: 14px; position: relative; z-index: 1; -ms-zoom: 1;">
<div id="qm_con_body">
<div class="emailbox" style="height: auto;min-height: 100px;word-wrap: break-word;font-size: 14px;font-family: "lucida Grande", Verdana, "Microsoft YaHei";" id="mailContentContainer"> <br><br>
<table align="center" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="left" valign="top">
<table width="500" border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="left" valign="top"><img width="127" height="28" src="cid:taizi_logo.png" border="0">td>
<td align="right" valign="top">[email protected]td>
[lqs-##]<td align="right" valign="top"><img width="328" height="44" src="cid:right_logo" border="0">td>
<td align="right" valign="top"> td>
tr>
<tr>
<td colspan="2"><img width="500" height="1" src="cid:email_line" border="0">td>
tr>
<tr height="24">
<td height="24" colspan="2">
tr>
<tr>
<td style="color: rgb(51, 51, 51); font-family: Geneva,Verdana,Arial,Helvetica; font-size: 10px; font-weight: normal;" colspan="2">
${content}
td>
tr>
<tr height="24">
<td height="24" colspan="2">
tr>
<tr>
<td colspan="2"><img width="500" height="1" src="cid:email_line" border="0">td>
tr>
<tr height="36">
<td height="24" colspan="2">
tr>
<tr>
<td class="message" style="color: rgb(0, 0, 0); font-family: Geneva,Verdana,Arial,Helvetica; font-size: 9px;" colspan="2"> <b>T̶a̶i̶z̶i̶ Inc.b><br>Organization:T̶a̶i̶z̶i̶
<a href="https://user.qzone.qq.com/1194895584" target="_blank">T̶a̶i̶z̶i̶a><br><br> 获取常见问题的解答,请咨询T̶a̶i̶z̶i̶
<a href="https://user.qzone.qq.com/1194895584" target="_blank">https://user.qzone.qq.com/1194895584a>
td>
tr>
table>
td>
tr>
<tr height="75">
<td height="75">
tr>
<tr>
<td>
<table align="center">
<tr>
<td width="500" align="center" class="message" style="color: rgb(51, 51, 51); font-family: Geneva,Verdana,Arial,Helvetica; font-size: 9px;">
<a href="https://user.qzone.qq.com/1194895584" target="_blank">T̶a̶i̶z̶i̶a> <br><br>td>
tr>
<tr>
<td width="500" align="center" class="message" style="color: rgb(51, 51, 51); font-family: Geneva,Verdana,Arial,Helvetica; font-size: 9px;"> T̶a̶i̶z̶i̶尊重你的隐私权。<br>与您个人信息有关的规定见
<a href="https://user.qzone.qq.com/1194895584" target="_blank">https://user.qzone.qq.com/1194895584a> <br><br>td>
tr>
<tr>
<td width="500" align="center" class="disclaimer" style="color: rgb(110, 110, 110); font-family: Geneva,Verdana,Arial,Helvetica; font-size: 9px;"> Copyright©2017LUO YuCheng.
<a href="https://user.qzone.qq.com/1194895584" target="_blank">保留所有权利a>
td>
tr>
table>
td>
tr>
table><br><br> <img src="cid:email_spacer">div>
div>
div>
div>
使用 email.properties 配置:
## 尝试使用AUTH命令认证用户
mail.smtp.auth=true
## 服务协议
mail.transport.protocol=smtp
## 邮件服务器
mail.smtp.host=smtp.163.com
## 使用 JSSE 的 SSL socketfactory 来取代默认的 socketfactory
mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
## false:只处理 SSL 的连接,对于非 SSL 的连接不做处理. true:处理 SSL 和非 SSL 的连接
mail.smtp.socketFactory.fallback=false
## 端口号
## mail.smtp.port=25
mail.smtp.port=465
## 服务邮箱
username=xxx
## 邮箱密码
password=xxx
## 发送人名称
senderNick=T̶a̶i̶z̶i̶
## logUrl 请以逗号分隔,且除去后缀后的名称和模板中的名称一致,不一致则不会替换,
## 会出现 tcmime.9162.9263.10034.bin 此类型的附件,凡是有图片参数的,请在模板设置对应的替换字段 cid:name
logoUrl=taizi_logo.png,email_line.gif,email_spacer.gif
其中读取文件代码及加载到 JAVA MAIL 的 session 中:
public EmailUtils(){
pro = new Properties();
try {
if(!fileIsTrue(filePath)){
System.out.println("配置文件不存在");
return;
}
in = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"));
if(in == null) return;
pro.load(in);//加载配置文件
in.close();//关闭流
} catch (FileNotFoundException e) {
System.out.println("文件不存在:"+e.toString());
} catch (IOException e) {
System.out.println("IO 流关闭异常:"+e.toString());
} finally {
if(in != null){
try {
in.close();//关闭流
} catch (IOException e) {
System.out.println("IO 流关闭异常:"+e.toString());
}
}
}
/*MailSSLSocketFactory msf = null;//SSL 加密
try {
msf = new MailSSLSocketFactory();
msf.setTrustAllHosts(true);
} catch (GeneralSecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
pro.put("mail.smtp.ssl.socketFactory",msf);*/
//尝试使用AUTH命令认证用户
auth = pro.getProperty("mail.smtp.auth").toString();
//装载服务协议、服务器
protocol = pro.getProperty("mail.transport.protocol").toString();
host = pro.getProperty("mail.smtp.host").toString();
//装载发件人昵称
senderNick = pro.getProperty("senderNick").toString();
//装载服务邮箱和密码
username = pro.getProperty("username").toString();
password = pro.getProperty("password").toString();
//装载 logo 图标路径
logoUrl = pro.getProperty("logoUrl").toString();
//建立会话
session = Session.getDefaultInstance(pro);
session.setDebug(true);
}
使用 XML 配置:
"1.0" encoding="UTF-8"?>
.smtp.auth="true" mail.transport.protocol="smtp"
mail.smtp.host="smtp.163.com" mail.smtp.port="465" mail.smtp.socketFactory.class="javax.net.ssl.SSLSocketFactory"
mail.smtp.socketFactory.fallback="false" username="xxx" password="xxx" senderNick="T̶a̶i̶z̶i̶"
logoUrl="taizi_logo.png,email_line.gif,email_spacer.gif">
加载 XML 中的配置进 Session:
public EmailUtils(){
pro = new Properties();
SAXBuilder builder = new SAXBuilder(false);
Document doc = new Document();//解析XML配置文件
try {
doc = builder.build(filePath);
//获取根元素
root = (Element)doc.getRootElement().getChildren().get(0);
//尝试使用AUTH命令认证用户
auth = root.getAttributeValue("mail.smtp.auth");
//装载服务协议、服务器
protocol = root.getAttributeValue("mail.transport.protocol");
host = root.getAttributeValue("mail.smtp.host");
//装载端口
port = root.getAttributeValue("mail.smtp.port");
//使用 JSSE 的 SSL socketfactory 来取代默认的 socketfactory
socketFactoryclass = root.getAttributeValue("mail.smtp.socketFactory.class");
//false:只处理 SSL 的连接,对于非 SSL 的连接不做处理. true:处理 SSL 和非 SSL 的连接
socketFactoryfallback = root.getAttributeValue("mail.smtp.socketFactory.fallback");
//装载服务邮箱和密码
username = root.getAttributeValue("username");
password = root.getAttributeValue("password");
//装载发件人昵称
senderNick = root.getAttributeValue("senderNick");
//装载 logo 图标
logoUrl = root.getAttributeValue("logoUrl");
} catch (JDOMException e) {
System.out.println("JDOMException 异常:"+e.toString());
} catch (IOException e) {
System.out.println("IO 异常:"+e.toString());
}
/*~采用JSSE SSL 加密代替 SSL 加密~*/
/*MailSSLSocketFactory msf = null;//SSL 加密
try {
msf = new MailSSLSocketFactory();
msf.setTrustAllHosts(true);
} catch (GeneralSecurityException e) {
System.out.println("GeneralSecurityException 异常:"+e.toString());
}
pro.put("mail.smtp.ssl.socketFactory",msf);*/
pro.put("mail.smtp.auth", auth);//auth 认证
pro.put("mail.transport.protocol", protocol);//协议
pro.put("mail.smtp.host", host);//服务器
pro.put("mail.smtp.port", port);//端口
pro.put("mail.smtp.socketFactory.class", socketFactoryclass);//加密方式 JSSE SSL
pro.put("mail.smtp.socketFactory.fallback", socketFactoryfallback);//是否同时处理非加密连接
pro.put("username", username);//发送邮箱
pro.put("password", password);//密码
//建立会话
session = Session.getDefaultInstance(pro);
session.setDebug(false);
}
配置文件加载进 Session 中之后,就可以开始发送邮件了,可以直接在main 方法中测试,前提是先配置好邮箱服务器,配制方法详见百度,x相关的变量,请自行增加或者下载附件
详见代码:
static{
rpath = getRealPath();//src 目录
filePath = rpath+PROPERTIESNAME;//配置文件路径
}
/**
*
* @Title: getInstance
* @Description: TODO 单列
* @return
* @return: EmailUtils
*/
public static EmailUtils getInstance() {
if (instance == null) {
instance = new EmailUtils();
}
return instance;
}
/**
*
* @Title: sendMail
* @Description: TODO 邮件发送
* @param to 收件人
* @param copyto 抄送人[可为空数组或者null]
* @param subject 主题
* @param content 正文
* @param logoarr html 邮件中的图片,名称需要和模板中的名称相同,
* 如模板中为 cid:top_logo,那么图片名称就应该是 top_logo.xxx
* @param fileList 附件列表
* @return
* @return: boolean
*/
private boolean sendMail(String[] to, String[] copyto, String subject, String content, String[] logoarr, String[] fileList) {
boolean success = true;
try {
mimeMsg = new MimeMessage(session);
mp = new MimeMultipart();
String nick = "";//自定义发件人昵称
try {
nick = MimeUtility.encodeText(senderNick);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
mimeMsg.setFrom(new InternetAddress(username, nick));//设置发件人
//设置收件人
if (to != null && to.length > 0) {
String toListStr = getMailList(to);
System.out.println("收件人:"+toListStr);
mimeMsg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(toListStr));
}
//设置抄送人
if (copyto != null && copyto.length > 0) {
String ccListStr = getMailList(copyto);
System.out.println("抄送人:"+ccListStr);
mimeMsg.setRecipients(Message.RecipientType.CC, InternetAddress.parse(ccListStr));
}
mimeMsg.setSubject(subject);//设置主题
MimeBodyPart bp = new MimeBodyPart();//设置正文
String body = content;
bp.setContent(body, "text/html;charset=utf-8");
mp.addBodyPart(bp);
for (int i = 0; i < logoarr.length; i++) {
String sUrl = rpath+File.separator+logoarr[i];
String str = logoarr[i].substring(0, logoarr[i].lastIndexOf("."));
boolean flag = fileIsTrue(sUrl);
if(flag){
//正文的图片部分
MimeBodyPart jpgBody = new MimeBodyPart();
FileDataSource fds = new FileDataSource(sUrl);
jpgBody.setDataHandler(new DataHandler(fds));
jpgBody.setContentID(str);
mp.addBodyPart(jpgBody);
}else{
System.out.println("logo 不存在:"+logoarr[i]);
}
}
//设置附件
if (fileList != null && fileList.length > 0) {
for (int i = 0; i < fileList.length; i++) {
boolean flag = fileIsTrue(fileList[i]);
if(!flag){
System.out.println("此附件不存在:"+fileList[i]);
continue;
}
bp = new MimeBodyPart();
FileDataSource fds_file = new FileDataSource(fileList[i]);
bp.setDataHandler(new DataHandler(fds_file));
String filename = MimeUtility.encodeText(fds_file.getName(), "UTF-8", "B");
filename = filename.replaceAll("\r","").replaceAll("\n","");
bp.setFileName(filename);
mp.addBodyPart(bp);
}
}
mimeMsg.setContent(mp);
mimeMsg.saveChanges();
//发送邮件
if (auth.equals("true")) {
Transport transport = session.getTransport(protocol);
transport.connect(host, username, password);
transport.sendMessage(mimeMsg, mimeMsg.getAllRecipients());
transport.close();
} else {
Transport.send(mimeMsg);
}
System.out.println("邮件发送成功");
} catch (MessagingException e) {
System.out.println("邮件发送异常:"+e.toString());
success = false;
} catch (UnsupportedEncodingException e) {
System.out.println("编码异常:"+e.toString());
success = false;
}
return success;
}
/**
*
* @Title: readEamilModel
* @Description: TODO 读取模板,路径存在返回,不存在返回 null
* @param modelPath 模板路径
* @return
* @return: String
*/
private static String readEamilModel(String modelPath){
sbf = new StringBuffer();
String modelFilePath = rpath+modelPath;
boolean flag = fileIsTrue(modelFilePath);
if(!flag){
System.out.println("路径不正确:"+rpath+",或模板不存在:"+modelPath);
return null;
}
InputStreamReader in = null;
BufferedReader br = null;
try {
in = new InputStreamReader(new FileInputStream(modelFilePath));
br = new BufferedReader(in);
String line=null;
while((line=br.readLine()) != null){
int index = line.indexOf("[lqs-##]");
if(index != -1){
System.out.println("过滤注释行:"+line);
line = line.substring(0, index);
if("".equals(line))continue;
}
sbf.append(line).append("\n");
}
} catch (FileNotFoundException e) {
System.out.println("IO 异常:模板不存在!"+e.toString());
} catch (IOException e) {
System.out.println("IO 异常:获取模板流出现异常!"+e.toString());
} finally {
try {
if(br != null){
br.close();
}
if(in != null){
in.close();
}
} catch (IOException e) {
System.out.println("IO 异常:关闭流出现异常!"+e.toString());
}
}
return sbf.toString();
}
/**
*
* @Title: getMailList
* @Description: TODO 收件人地址处理
* @param mailArray
* @return
* @return: String
*/
private String getMailList(String[] mailArray){
sbf = new StringBuffer();
int length = mailArray.length;
if(mailArray != null && length < 2){
sbf.append(mailArray[0]);
}else{
for(int i = 0; i < length; i++){
sbf.append(mailArray[i]);
if(i != (length - 1)){
sbf.append(",");
}
}
}
return sbf.toString();
}
/**
*
* @Title: fileIsTrue
* @Description: TODO 判断文件路径是否存在
* @param filePath 文件路径
* @return
* @return: boolean
*/
public static boolean fileIsTrue(String filePath){
File file = new File(filePath);
if(file.exists())return true;
return false;
}
/**
*
* @Title: sendEmailCase
* @Description: TODO 邮件参数处理
* @param emailmodel 模板全名+后缀
* @param copyto 抄送人
* @param subject 主题
* @param content 正文
* @param fileList 附件列表
* @param to 收件人
* @return
* @return: boolean
*/
public boolean sendEmailCase(String emailmodel, String[] copyto, String subject, String content, String[] fileList, String... to){
/*******************************模板处理*******************************/
if(emailmodel != null){//测试模板是否存在,不存在的话直接使用正文作为邮件发送内容
int indexof = emailmodel.lastIndexOf(".");
if(indexof == -1){
System.out.println("邮件模板异常:["+emailmodel+"],将不使用模板!");
}else{
if(!"model".equals(emailmodel.substring(indexof+1, emailmodel.length()))){//拿到模板后缀
System.out.println("邮件模板后缀异常:["+emailmodel+"],将不使用模板!");
}else{
emailmodel = readEamilModel(emailmodel);//获取模板
if(emailmodel == null){
System.out.println("邮件模板为空,将不使用模板!");
}else{
content = StringUtils.replace(emailmodel, "${content}", content);//替换模板中的正文
}
}
}
}else{
System.out.println("邮件模板异常:[NULL],将不使用模板!");
}
/*******************************收件人处理*******************************/
List toFrom = null;//用于接收处理过的收件人
int k=0;
do{//k==1 处理收件人,k>1 处理转送人
k++;
String[] stri;
toFrom = new ArrayList();
if(k==1){
stri = to;
}else{
stri = copyto;
}
for (int i = 0; i < stri.length; i++) {
String str = stri[i];
str = str.replaceAll(REGEX, "");//去除空格
Pattern r = Pattern.compile(PATTERN);//匹配邮箱
Matcher m = r.matcher(str);
if(m.matches()){
toFrom.add(str);
}
}
int size = toFrom.size();
if(size<1 && k==1){
System.out.println("无收件人,邮件发送失败");
return false;
}else if(size<1 && k>1){
System.out.println("此邮件无需转送他人");
copyto = null;
}else{
if(k==1){
to = (String[])toFrom.toArray(new String[size]);
}else if(k>1){
copyto = (String[])toFrom.toArray(new String[size]);
}
}
}while(k<2);
/*******************************正文图片处理*******************************/
String[] logoarr = logoUrl.split(",");//配置文件的图片路径
if(logoarr.length > 0){
List logolist = new ArrayList(Arrays.asList(logoarr));
Iterator it = logolist.iterator();
while(it.hasNext()){
String str = it.next();
int num = str.indexOf(".");//判断文件是否有 . 存在
if(num == -1){
it.remove();
}
}
logoarr = logolist.toArray(new String[logolist.size()]);
}
return sendMail(to, copyto, subject, content, logoarr, fileList);
}
/**
*
* @Title: getRealPath
* @Description: TODO 获取项目 src 路径
* @return
* @return: String
*/
private static String getRealPath(){
rpath = EmailUtils.class.getResource("/").getPath().toString();
rpath = rpath.substring(1, rpath.length());
rpath = rpath.replace("\\","");
rpath = rpath.replace("/","\\");
/*realPath = realPath.replace("file:","");
realPath = realPath.replace("classes\\","");
String path = Thread.currentThread().getContextClassLoader().getResource("").toString();
path = path.replace("/","\\");
path = path.replace("file:","");
path = path.replace("classes\\","");
path = path.substring(1);
*/
return rpath;
}
/**
* @Title: getRealPath
* @Description: TODO 获取项目真实路径+后缀
* @param fileName
* @return
* @return: String
*/
public static String getRealPath(String fileName){
return getRealPath()+fileName;
}
/**
*
* @Title: main
* @Description: TODO 测试
* @param args
* @return: void
*/
public static void main(String[] args) {
EmailUtils eu = EmailUtils.getInstance();
eu.configurationFile();//测试配置文件
String[] to = {"[email protected]","[email protected]","[email protected]"};//收件人
String[] copyto = {"[email protected]"};//转送人
String subject = "T̶a̶i̶z̶i̶";//主题
sbf = new StringBuffer();
sbf.append("您好:如果有一天:
你不再寻找爱情,只是去爱;
你不再渴望成功,只是去做;
你不再追求空泛的成长,只是开始修养自己的性情;
你人生的一切,才真正开始。"
);
sbf.append("Taizi 长情。");
sbf.append("Luoyc
");
sbf.append("此致
Luoyc
");
String content = sbf.toString();//正文
String emailmodel = "email.model";//模板名称.后缀
List list = new ArrayList();//附件地址
list.add(getRealPath()+"TaiziLuoyc.gif");
int size = list.size();
String[] fileList = (String[])list.toArray(new String[size]);
eu.sendEmailCase(emailmodel, copyto, subject, content, fileList, to);
}
/**
*
* @Title: configurationFile
* @Description: TODO 测试配置文件
* @return: void
*/
private void configurationFile(){
System.out.println("当前路径:"+rpath);
System.out.println("配置文件路径:"+filePath);
Set spn = pro.stringPropertyNames();
if(spn.size() == 0){
System.out.println("无数据");
return;
}
Iterator it = spn.iterator();
System.out.println("打印配置文件参数:");
while(it.hasNext()){
String key = it.next();
System.out.println(key+":"+pro.getProperty(key));
}
}
至此,邮件发送结束,代码块的逻辑很简单,就不赘述了。