前段时间,有兴趣去研究了一下基于java的邮件发送功能,还颇有趣味,在此做出一个分享。
java提供了一个jar包,javax.mail专门用来做邮件功能,当然Spring也集成了org.springframework.mail包和子包org.springframework.mail.javamail来提供邮件的封装。我这里使用的是SpringMVC环境,所以集成spring工具包来说方便一些。
freemark主要是用来制作邮件模板,因为发送的邮件可能需要一些样式布局什么的,所有模板就显得尤为重要了。
参考博客:
- http://www.cnblogs.com/gossip/p/5887613.html
- http://uule.iteye.com/blog/1094365
- http://blog.csdn.net/zdp072/article/details/32745335
一:利用freemark集成邮件模板
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.9</version>
<type>pom</type>
</dependency>
spring-mvc.xml是spring的配置文件,可能你的配置叫applicationContext.xml 不重要,重要的是你知道他是一个bean就行了,配到<beans></beans>里面。
<!-- Freemarker配置 -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/" /> <!--配置模板文件存放路径-->
<property name="freemarkerSettings">
<props>
<prop key="locale">zh_CN</prop> <!--其他相关配置,请参考freemark教程-->
<prop key="template_update_delay">1800</prop>
<prop key="default_encoding">UTF-8</prop>
<prop key="number_format">0.##########</prop>
<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
<prop key="classic_compatible">true</prop>
<prop key="template_exception_handler">ignore</prop>
</props>
</property>
</bean>
3.制作freemark模板
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf8">
<title>邮件模板</title>
</head>
<body>
<style type="text/css">
table,table tr th, table tr td { border:1px solid #ccc; }
table { width: 600px; min-height: 25px; line-height: 25px; border-collapse: collapse; margin:10px 20px}
.td1{
text-align:center;width:100px;
}
.gogo{
width:86px;height:30px;background-color:#009688;font-size:10px;text-align:center;line-height:30px;cursor:pointer;margin-bottom:10px
}
.gogo a{
color:#ffffff;
}
</style>
<div style="width:80%;height:530px;margin:0 auto;border:1px solid #ccc;border-bottom:none;">
<div style="width:100%;height:58px;background-color:#393D49;line-height:58px">
<div style="width:45px;height:45px;background:url('cid:note') center no-repeat;margin:7px 0 0 10px;float:left"></div>
<a style="color:#009688;margin-left:10px;font-family:黑体;font-size:25px;float:left">XX后台管理系统</a>
</div>
<div style="height:222px;padding:10px 20px;color:#393D49">
<i>Hi,亲爱的XX同事:</i>
<div style="margin:20px 0">您好,特此通知您参加XX会议</div>
<table>
<tr>
<td class="td1">【会议名称】</td>
<td>${className}</td>
</tr>
<tr>
<td class="td1">【会议时间】</td>
<td>${classDate}</td>
</tr>
<tr>
<td class="td1">【会议地点】</td>
<td>${classAddr}</td>
</tr>
<tr>
<td class="td1">【会议积分】</td>
<td>${classScore}分</td>
</tr>
<tr>
<td class="td1">【会议描述】</td>
<td>${classDesc}</td>
</tr>
<#if (other!='')>
<tr>
<td class="td1">【其他】</td>
<td>${other}</td>
</tr>
</#if>
</table>
<div class="gogo"><a href="${url}">了解详情</a></div>
<ul>
<li>
<i>会议成绩和积分将影响员工绩效等,请务必准时参加。</i>
</li>
<li>
<i>更多会议信息,请登录<a href="${url}" style="color:#009688">XX后台管理系统</a>,或直接访问:<span style="color:#009688">${url}</span></i>
</li>
<li>
<i>如有疑问,请咨询管理员<span style="color:#FF5722">@${author}</span></i>
</li>
<li>
<i>本邮件由XX后台管理系统发出,请勿直接回复!祝您工作愉快,谢谢!</i>
</li>
</ul>
</div>
</div>
<div style="width:80%;height:30px;line-height:30px;color:#ccc;font-size:12px;margin:0 auto;text-align:center;border:1px solid #ccc;border-top:none;background-color:#FBFBFB">
<span>©XXXXXX有限公司</span>
</div>
</body>
</html>
模板中利用${ }来动态的插入参数,利用<#if (other!=' ')></if>来做条件判断。模板中嵌入图片background:url('cid:note')import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.SendFailedException;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.mail.MailSendException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import com.sun.mail.util.MailConnectException;
import freemarker.template.Template;
/**
*
* @ClassName: EmailUtil
* @Description: 邮件操作工具类
* @author aggerChen
* @date 2017年7月31日 上午10:16:15
* @version 0.0.1
*/
public class EmailUtil{
/**
*
* @MethodName: getMailText
* @Description: 获取邮件文本(通过加载FreeMarker模板HTML)
* @param freemarkerConfig 配置文件
* @param freemarkerFileName 模板名 eg: "classNotice.html"
* @param map 模板动态数据值
* @return
* @throws Exception String
*/
public static String getMailText(FreeMarkerConfigurer freemarkerConfig,
String freemarkerFileName,Map<String,String> map) throws Exception {
// 通过指定模板名获取FreeMarker模板实例
Template template = freemarkerConfig.getConfiguration().getTemplate(freemarkerFileName);
// 解析模板并替换动态数据,最终content将替换模板文件中的${content}标签。
String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
return htmlText;
}
}
这个工具类我们后面还会添加其他方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
/**
*
* @ClassName: ClassesController
* @Description: 课程管理
* @author aggerChen
* @date 2017年4月10日 上午10:01:34
*/
@Controller
@RequestMapping("classes")
public class ClassesController{
@Autowired
private FreeMarkerConfigurer freemarkerConfig; //依赖注入spring-mvc.中配置的bean 邮件模板配置
/**
*
* @MethodName: sendEmail
* @Description: 发送邮件通知
*/
@RequestMapping("/sendEmail")
@ResponseBody
public Result sendEmail(Long classId,String addr,String other,HttpServletRequest request,HttpSession session){
Result result = new Result(-1, "通知邮件发送失败!");
//用Map存储变量名和变量值映射,用于注入模板中${}引用的变量
Map<String, String> map = new HashMap<>();
map.put("className", "关于拉格朗日中值定理与牛顿-莱布尼茨公式在日常代码中的应用会议"); //会议名
map.put("classDate", "2017-08-30 15:23:00"); //会议时间
map.put("classAddr", "会议室"); //会议地点
map.put("classScore", 5); //会议积分
map.put("author", "管理员"); //邮件作者
map.put("classDesc", "描述描述描述"); //描述
map.put("other", "无"); //其他
map.put("url", "http://www.xxx.com"); //项目访问地址
//调用工具类,传入参数生成模板String
String text = EmailUtil.getMailText(freemarkerConfig, "classNotice.html", map);
//生成text备用,以便后续传递给邮件发送。还有,模板中有一个图片,也需要我们注入,后面慢慢讲来
...
}
}
二:通过org.springframework.mail包发送模板邮件
要试用spring封装包发送邮件,则需要引入一下相关jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.5</version>
</dependency>
#邮件发送配置
emailHost:smtp.exmail.qq.com
emailPort:587
emailFrom:[email protected]
emailPwd:xxxxx
/**
*
* @ClassName: EmailConfig
* @Description: 邮件配置类
* @author aggerChen
* @date 2017年8月2日 下午3:13:21
*/
public static class EmailConfig{
private String host; //发送邮件的host
private int port; //发送端口
private String from; //邮件发送者
private String fromPwd; //邮件发送者密码
private String[] toArr; //邮件接收者数组
private String[] CcArr; //邮件抄送数组
private String subject; //邮件主题
private String text; //邮件文本
private Map<String,File> inLineMap; //嵌入到模板中的图片
//无参构造
public EmailConfig() {
super();
this.inLineMap = new HashMap<>();
}
//重载构造
public EmailConfig(String host, int port, String from, String fromPwd,String[] toArr,String[] CcArr, String subject, String text) {
this();
this.host = host;
this.port = port;
this.from = from;
this.fromPwd = fromPwd;
this.toArr = toArr;
this.CcArr = CcArr;
this.subject = subject;
this.text = text;
}
/**
* 重载构造
* @Description: 通过依赖配置文件获取host和port
* @param from
* @param fromPwd
* @param to
* @param toArr
* @param CcArr
* @param subject
* @param text
*/
public EmailConfig(String from, String fromPwd,String[] toArr,String[] CcArr, String subject, String text) {
this(null,-1,from,fromPwd,toArr,CcArr,subject,text);
try {
this.host = SystemConfig.getResource("emailHost"); //调用了另外一个工具类SystemConfig来获取我们在config.properties中配置的信息。
this.port = Integer.parseInt(SystemConfig.getResource("emailPort"));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 重载构造
* @Description: 通过依赖配置文件获取host和port,from,pwd
* @param toArr 邮件接收者数组 eg: {"[email protected]","[email protected]"}
* @param CcArr 邮件抄送者数组 eg: {"[email protected]","[email protected]"}
* @param subject 邮件主题
* @param text 邮件正文
*/
public EmailConfig(String[] toArr,String[] CcArr, String subject, String text) {
this(null,-1,null,null,toArr,CcArr,subject,text);
try {
this.host = SystemConfig.getResource("emailHost");
this.port = Integer.parseInt(SystemConfig.getResource("emailPort"));
this.from = SystemConfig.getResource("emailFrom");
this.fromPwd = SystemConfig.getResource("emailPwd");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 重载构造
* @Description: 通过依赖配置文件获取host和port,from,pwd
* @param toArr 邮件接收者数组 eg: {"[email protected]","[email protected]"}
* @param subject 邮件主题
* @param text 邮件正文
*/
public EmailConfig(String[] toArr, String subject, String text) {
this(null,-1,null,null,toArr,null,subject,text);
try {
this.host = SystemConfig.getResource("emailHost");
this.port = Integer.parseInt(SystemConfig.getResource("emailPort"));
this.from = SystemConfig.getResource("emailFrom");
this.fromPwd = SystemConfig.getResource("emailPwd");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @MethodName: putInLineVal
* @Description: 向配置中追加模板参数
* @param key
* @param file void
*/
public void putInLineVal(String key,File file){
this.inLineMap.put(key, file);
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getFromPwd() {
return fromPwd;
}
public void setFromPwd(String fromPwd) {
this.fromPwd = fromPwd;
}
public String[] getToArr() {
return toArr;
}
public void setToArr(String[] toArr) {
this.toArr = toArr;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public Map<String, File> getInLineMap() {
return inLineMap;
}
public void setInLineMap(Map<String, File> inLineMap) {
this.inLineMap = inLineMap;
}
public String[] getCcArr() {
return CcArr;
}
public void setCcArr(String[] ccArr) {
CcArr = ccArr;
}
}
import java.io.File;
import javax.mail.Address;
import javax.mail.MessagingException;
import javax.mail.SendFailedException;
import javax.mail.internet.MimeMessage;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.mail.MailSendException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import com.sun.mail.util.MailConnectException;
import freemarker.template.Template;
/**
*
* @ClassName: EmailUtil
* @Description: 邮件操作工具类
* @author aggerChen
* @date 2017年7月31日 上午10:16:15
* @version 0.0.1
*/
public class EmailUtil{
private static final int SUCC = 0; //成功 //自定义的一些状态码,用于发生异常时返回
private static final int FAIL = -1; //失败
private static final int AUTHENTICATION = -2; //发送者身份验证异常
private static final int CONNECT_ERR = -3; //网络异常
private static final int NULL_MAIL = -4; //收件人空指针异常
/**
*
* @MethodName: sendMailByFreeMarker
* @Description: 通过FreeMarker模板发送邮件
* @param config void 邮件配置文件
*/
public static Result sendMailByFreeMarker(EmailConfig config) { //通过freeMark发送邮件,参数为我们定义的EmailConfig邮件参数类
Result result = new Result(SUCC, "邮件发送成功!");
try {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(config.getHost()); //设置Host
mailSender.setPort(config.getPort()); //设置Port
mailSender.setUsername(config.getFrom()); //设置发送者邮箱
mailSender.setPassword(config.getFromPwd()); //设置发送者邮箱密码
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true,"UTF-8");
helper.setFrom(config.getFrom());
String[] to = config.getToArr(); //收件人邮箱数组
String[] Cc = config.getCcArr(); //抄送人邮箱数据
if((to==null&&Cc==null)||(to.length<=0&&Cc.length<=0)){
result.setResultCode(NULL_MAIL);
result.setMsg("收件人和抄送人不能同时为空!");
return result;
}
if(to!=null&&to.length>0) helper.setTo(to); //接收
if(Cc!=null&&Cc.length>0) helper.setCc(Cc); //抄送
helper.setSubject(config.getSubject()); //设置邮件主题
helper.setText(config.getText(), true); //设置邮件正文 第二个参数true,表示是否是html,我们的text是获取的模板html,所以为true
//图片嵌入到html文件中
Map<String,File> map = config.getInLineMap(); //邮件模板中有嵌入图片,请看邮件左上角的记事本图片。需要我们在这里配置。
if(map!=null){
for (String key : map.keySet()) {
helper.addInline(key, map.get(key));
}
}
mailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
result.setMsg("邮件发送失败!");
result.setResultCode(FAIL);
return result;
} catch (MailSendException e) { //邮件发送异常,里面包含了网络连接异常,邮箱地址错误异常,空指针异常等,所有获取异常信息,为用户返回更详细的异常信息
e.printStackTrace();
Exception ex = e.getMessageExceptions()[0];
if(ex instanceof MailConnectException){ //网络连接异常
result.setResultCode(CONNECT_ERR);
result.setMsg("网络连接异常!");
return result;
}else if(ex instanceof SendFailedException){ //邮箱地址错误异常.因为当收件人数组中只有有一个邮箱错误,则整个邮件就发不出去,这是我们不想看到的。我希望的是让他剔除错误的邮箱反馈给用户,而正确的邮箱则继续发送
Address[] address = ((SendFailedException) ex).getInvalidAddresses(); //获取错误邮箱数组
return removeErrMailResend(address, config); //调用移除错误邮箱并重新发送邮件
}else if(ex instanceof NullPointerException){
result.setResultCode(NULL_MAIL);
result.setMsg("收件人为空!");
return result;
}
} catch (MailAuthenticationException e) { //身份认证异常
e.printStackTrace();
result.setResultCode(AUTHENTICATION);
result.setMsg("发件箱配置或密码错误!");
return result;
} catch (Exception e) { //其他异常
e.printStackTrace();
result.setMsg("邮件发送失败!");
result.setResultCode(FAIL);
return result;
}
return result;
}
/**
*
* @MethodName: getMailText
* @Description: 获取邮件文本(通过加载FreeMarker模板HTML)
* @param freemarkerConfig 配置文件
* @param freemarkerFileName 模板名 eg: "classNotice.html"
* @param map 模板动态数据值
* @return
* @throws Exception String
*/
public static String getMailText(FreeMarkerConfigurer freemarkerConfig,
String freemarkerFileName,Map<String,String> map) throws Exception {
// 通过指定模板名获取FreeMarker模板实例
Template template = freemarkerConfig.getConfiguration().getTemplate(freemarkerFileName);
// 解析模板并替换动态数据,最终content将替换模板文件中的${content}标签。
String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
return htmlText;
}
/**
*
* @MethodName: removeErrMailResend
* @Description: 移除错误邮箱并重新调用发送邮件
* @param address 错误邮箱数组
* @param config 邮件配置
* @return Result 返回结果(result.result:错误邮箱数组)
*/
private static Result removeErrMailResend(Address[] address,EmailConfig config){
Result result = new Result();
if(address==null||address.length<=0) return result;
String[] toArr = config.getToArr();
String[] CcArr = config.getCcArr();
for (int i=0,len=address.length;i<len;i++) {
String a = address[i].toString();
toArr = ArrayUtils.removeElement(toArr, a);
CcArr = ArrayUtils.removeElement(CcArr, a);
}
config.setToArr(toArr);
config.setCcArr(CcArr);
Result newResult = sendMailByFreeMarker(config); //递归调用重新发送邮件
result.setResultCode(newResult.getResultCode());
result.setMsg(newResult.getMsg());
result.setResult(address); //返回错误邮箱数组对象,提示前台用户
return result;
}
}
在上文controller的sendEmail方法中,我们已经获取模板到模板字符串,接下来就是发送邮件了, 加入如下代码:
import java.io.File;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import json.Result;
import json.SessionInfo;
import json.jfTableVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
/**
*
* @ClassName: ClassesController
* @Description: 课程管理
* @author aggerChen
* @date 2017年4月10日 上午10:01:34
*/
@Controller
@RequestMapping("classes")
public class ClassesController extends BaseController{
@Autowired
private FreeMarkerConfigurer freemarkerConfig; //依赖注入spring-mvc.中配置的bean 邮件模板配置
/**
*
* @MethodName: sendEmail
* @Description: 发送邮件通知
*/
@RequestMapping("/sendEmail")
@ResponseBody
public Result sendEmail(Long classId,String addr,String other,HttpServletRequest request,HttpSession session){
Result result = new Result(-1, "通知邮件发送失败!");
try {
//获取项目当前web访问地址
String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()+request.getContextPath();
//用Map存储变量名和变量值映射,用于注入模板中${}引用的变量
Map<String, String> map = new HashMap<>();
map.put("className", "关于拉格朗日中值定理与牛顿-莱布尼茨公式在日常代码中的应用会议"); //会议名
map.put("classDate", "2017-08-30 15:23:00"); //会议时间
map.put("classAddr", "会议室"); //会议地点
map.put("classScore", 5); //会议积分
map.put("author", "管理员"); //邮件作者
map.put("classDesc", "描述描述描述"); //描述
map.put("other", "无"); //其他
map.put("url", "http://www.xxx.com"); //项目访问地址
//调用工具类,传入参数生成模板String
String text = EmailUtil.getMailText(freemarkerConfig, "classNotice.html", map);
//生成text备用,以便后续传递给邮件发送。还有,模板中有一个图片,也需要我们注入,后面慢慢讲来
EmailConfig config = new EmailConfig(toArr,CcArr, "会议通知", text); //创建EmailConfig邮件配置类,传入我们的基本参数
//图片嵌入到html文件中
String path = this.getClass().getResource("/").getPath().replaceAll("WEB-INF/classes/", ""); //邮件模板左上角图片地址,因为是web项目,所以在WEB-INF下获取静态图片资源
File file = new File(path+"dist/images/note.png");
config.putInLineVal("note", file); //向配置中追加模板内嵌参数
result = EmailUtil.sendMailByFreeMarker(config); //调用邮件工具类静态方法发送邮件。返回result 是我们自定义的一个结果类,其中包含resultCode,resultObj,resultMsg三个属性。
}
}