前段時間,有興趣去研究了一下基於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三個屬性。
}
}