Javax.Mail 工具类
2019-02-14 本文已影响0人
wang6771264
-
1.基础Model
package com.maple.common.utils.mail;
import javax.activation.DataHandler;
import java.util.List;
import java.util.Map;
import lombok.Data;
@Data
public class MailMessage {
/**
* 内容ID
*/
private String contentID;
/**
* 主题
*/
private String subject;
/**
* 内容(包括html)
*/
private String content;
/**
* 文本格式类型(例子:text/html; charset=utf-8)
*/
private String subType;
/**
* 图片传入,适用于HTML邮件,key:cid名称;value:文件路径
*/
private Map<String, String> images;
/**
* 附件上传
*/
private List<String> attachments;
/**
* 文件
*/
private DataHandler dh;
/**
* 描述
*/
private String description;
}
package com.maple.common.utils.mail;
import java.util.Map;
import lombok.Data;
/**
* 填充模板的邮件实体
*/
@Data
public class MailTmplMessage extends MailMessage {
private String tmplPath;
private Map<String, String> params;
}
-
2.辅助类
package com.maple.common.utils.mail;
import java.io.File;
import java.net.URL;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CacheEntity<T> {
Logger logger = LoggerFactory.getLogger(getClass());
Date date;
URL url;
long lastModified;
long hit;
long length;
T t;
public long getLastModified() {
return lastModified;
}
public void setLastModified(long lastModified) {
this.lastModified = lastModified;
}
public long getHit() {
return hit;
}
public void setHit(long hit) {
this.hit = hit;
}
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public boolean validate(File file) {
if (file != null) {
if (file.lastModified() != this.lastModified) {
return false;
}
if (file.length() != this.length) {
return false;
}
}
logger.debug("url {} hit {}", url, ++hit);
return true;
}
public CacheEntity(URL url, File file, T t) {
this.url = url;
this.date = new Date(System.currentTimeMillis());
this.hit = 0;
if (file != null) {
this.lastModified = file.lastModified();
this.length = file.length();
}
this.t = t;
}
public String toString() {
if (date == null) {
return "Hit #" + hit;
}
return "Hit #" + hit + " since " + date;
}
}
package com.maple.common.utils.mail;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.WeakHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbsFileCache<T> {
protected Logger logger = LoggerFactory.getLogger(getClass());
WeakHashMap<String, CacheEntity<T>> cache;
public AbsFileCache(int cacheSize) {
cache = new WeakHashMap<String, CacheEntity<T>>(cacheSize);
}
public T get(String path) throws IOException {
URL url = this.getClass().getClassLoader().getResource(path);
if (url != null) {
logger.info("load file:"+url);
String protocol = url.getProtocol();
File file = null;
InputStream is = null;
if ("jar".equals(protocol) || "zip".equals(protocol)) {
String jarFile = url.getFile();
int pos = jarFile.indexOf("!");
if (pos > 0) {
jarFile = jarFile.substring(0, pos);
}
file = new File(jarFile);
is = url.openStream();
} else if ("file".equals(protocol)) {
file = new File(url.getFile());
is = new FileInputStream(file);
} else {
logger.warn("not support the protocol {} with {}", protocol, url);
}
CacheEntity<T> ce = cache.get(path);
if (ce != null && ce.validate(file)) {
return ce.getT();
} else {
try {
T t = createCache(is);
ce = new CacheEntity<T>(url, file, t);
cache.put(path, ce);
return t;
} finally {
if (is != null) {
is.close();
}
}
}
} else {
logger.warn("not found the file {}", path);
}
return null;
}
protected abstract T createCache(InputStream is) throws IOException;
}
package com.maple.common.utils.mail;
import java.io.InputStream;
import java.io.InputStreamReader;
import groovy.text.StreamingTemplateEngine;
import groovy.text.Template;
public final class TemplateCache extends AbsFileCache<Template> {
StreamingTemplateEngine templateEngine = new StreamingTemplateEngine();
private TemplateCache() {
super(256);
}
private static class TemplateCacheHolder {
private static TemplateCache Ins = new TemplateCache();
}
public static TemplateCache getIns() {
return TemplateCacheHolder.Ins;
}
@Override
public Template createCache(InputStream is) {
Template t;
try {
t = templateEngine.createTemplate(new InputStreamReader(is, "UTF-8"));
return t;
} catch (Exception e) {
logger.error("create template error", e);
throw new RuntimeException(e);
}
}
}
-
3.邮件发送类
Props参数配置可以在https://javaee.github.io/javamail/docs/api/中找到,一般邮件发送都是用smtp/pop3即可。*
*使用groovy的模板引擎参数找不到会报错,不会填充完毕,最终选择使用Velocity来做,Spring有velociy的支持。
package com.maple.common.utils.mail;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.annotation.PostConstruct;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.event.MessageCountAdapter;
import javax.mail.event.MessageCountEvent;
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 java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import com.alibaba.fastjson.JSON;
import com.sun.mail.imap.IMAPFolder;
import groovy.text.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
/**
* 邮件工具类
*/
@Slf4j
@Component
public class MailService {
@Autowired
private Velocity velocity;
private static final String username = "xxxx@qq.com";
//此处需要注意的是,这里的密码是邮箱的第三方访问授权码,不是真正的邮箱密码
private static final String password = "sfhfaqvyvfhzdjii";
Authenticator auth = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
};
Properties props = new Properties();
@PostConstruct
public void init(){
//基础参数设置
props.put("mail.smtp.host", "smtp.qq.com");
props.put("mail.imap.host", "imap.qq.com");
//pop3参数设置
props.put("mail.pop3.user", "2294808913@qq.com");
props.put("mmail.pop3.host", "pop.qq.com");
props.put("mail.pop3.ssl.enable", true);
props.put("mail.pop3.port", 995);
props.put("mail.smtp.port", 465);
props.put("mail.smtp.ssl.enable", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.debug", "true");
}
public void sendTmplEmail(String recipients, MailTmplMessage msg, Message.RecipientType type){
StringWriter sw = new StringWriter();
try {
//参数找不到会报错,不可取
Template t = TemplateCache.getIns().get(msg.getTmplPath());
t.make(msg.getParams()).writeTo(sw);
msg.setContent(sw.toString());
} catch (IOException e) {
log.error("File tmpl assembly failed! params:{}", JSON.toJSONString(msg.getParams()), e);
}
sendMail(recipients, msg, type);
}
/**
* @param type:Message.RecipientType.TO常量表示收件人类型是邮件接收者
* Message.RecipientType.CC常量表示收件人类型是抄送者
* Message.RecipientType.BCC常量表示收件人的类型是密送着
*
*/
public void sendMail(String recipients, MailMessage msg, Message.RecipientType type) {
// set any other needed mail.smtp.* properties here
Session session = Session.getDefaultInstance(props, auth);
// set the message content here
// msg.setContent(mp);
try {
MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.setFrom(username);
mimeMessage.setSubject(msg.getSubject());
//HTML页面
if (msg.getSubType().contains("text/html")) {
Multipart multipart = new MimeMultipart();
mimeMessage.setContent(multipart);
MimeBodyPart htmlBody = new MimeBodyPart();
multipart.addBodyPart(htmlBody);
htmlBody.setContent(msg.getContent(), msg.getSubType());
//HTML内嵌图片
if (!CollectionUtils.isEmpty(msg.getImages())) {
addImages(multipart, msg.getImages());
}
}else{
mimeMessage.setText(msg.getContent());
}
//上传附件
if (!CollectionUtils.isEmpty(msg.getAttachments())) {
Multipart multipart;
if(mimeMessage.getContent() != null){
multipart = (MimeMultipart)mimeMessage.getContent();
}else{
multipart = new MimeMultipart();
}
for (String attachment : msg.getAttachments()) {
multipart.addBodyPart(createAttachment(attachment));
}
}
mimeMessage.setSentDate(new Date());
mimeMessage.setRecipients(type, recipients);
Transport transport = session.getTransport();
transport.send(mimeMessage, username, password);
} catch (MessagingException e) {
log.error("send mail failed! content! Email:{}", JSON.toJSONString(msg), e);
} catch (IOException e) {
log.error("create attachment failed! Email:{}", JSON.toJSONString(msg), e);
}
}
/**
* 上传附件
*/
private BodyPart createAttachment(String attachment){
try {
MimeBodyPart mbp = new MimeBodyPart();
DataSource fds = new FileDataSource(attachment);
mbp.setFileName(MimeUtility.encodeText(fds.getName()));
DataHandler dh = new DataHandler(fds);
mbp.setDataHandler(dh);
return mbp;
} catch (MessagingException e) {
log.error("add attachment failed! fileName:{}", attachment, e);
} catch (UnsupportedEncodingException e) {
log.error("file format error! fileName:{}", attachment, e);
}
return null;
}
/**
* html文件添加图片
* 注意:如果在html文本中未标记cid,文件会被当做附件处理
*/
private void addImages(Multipart multipart, Map<String, String> images) {
for (Entry<String, String> entry : images.entrySet()) {
MimeBodyPart imageBody = new MimeBodyPart();
try {
imageBody.setContentID(entry.getKey());
DataSource fds = new FileDataSource(entry.getValue());
imageBody.setFileName(MimeUtility.encodeText(fds.getName()));
DataHandler dh = new DataHandler(fds);
imageBody.setDataHandler(dh);
multipart.addBodyPart(imageBody);
} catch (MessagingException e) {
log.error("add image failed! ContentID:{}", entry.getKey(), e);
} catch (UnsupportedEncodingException e) {
log.error("file format error! fileName:{}", entry.getValue(), e);
}
}
}
/**
* 添加附件(文件形式添加)
* @param file
*/
public void addAttachment(File file){
DataSource ds = new FileDataSource(file) {
public String getContentType() {
return "mytype/mysubtype";
}
};
MimeBodyPart mbp = new MimeBodyPart();
try {
mbp.setDataHandler(new DataHandler(ds));
//早期的javamail可以使用该方法编码
mbp.setFileName(MimeUtility.encodeText(file.getName()));
//对应的解码
// String filename = MimeUtility.decodeText(mbp.getFileName());
mbp.setFileName(file.getName());
mbp.setDisposition(Part.ATTACHMENT);
} catch (MessagingException e) {
log.error("add attachment failed!", e);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**
* 添加附件(byte形式添加)
* @param data
*/
public void addAttachment(String data){
MimeBodyPart mbp = new MimeBodyPart();
data = "any ASCII data";
try {
DataSource ds = new ByteArrayDataSource(data, "application/x-any");
mbp.setDataHandler(new DataHandler(ds));
} catch (MessagingException e) {
log.error("add ASCII data failed!", e);
} catch (IOException e) {
log.error("read byte data failed! data:{}", data, e);
}
}
}
-
4.测试类
package com.maple.example;
import javax.mail.Message.RecipientType;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import com.maple.common.utils.mail.MailService;
import com.maple.common.utils.mail.MailTmplMessage;
import com.maple.common.utils.mail.TemplateCache;
import groovy.text.Template;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
//@SpringBootTest
public class StudyApplicationTests {
@Test
public void contextLoads() {
}
// @Autowired
private MailService mailService = new MailService();
@Test
public void sendMailTest() {
MailTmplMessage msg = new MailTmplMessage();
msg.setSubject("测试html邮件主题");
Map<String, String> map = new HashMap<>();
map.put("title", "测试邮件标题");
map.put("first", "大写头");
map.put("testMan", "王威");
msg.setParams(map);
//html照片
String userDir = System.getProperties().get("user.dir").toString();
Map<String, String> images = new HashMap<>();
images.put("image", userDir + "/src/main/resources/img/test_email.jpg");
images.put("email2", userDir + "/src/main/resources/img/email2.png");
msg.setImages(images);
msg.setTmplPath("templates/mail/testMail.html");
msg.setSubType("text/html; charset=utf-8");
msg.setDescription("Maple test send!");
mailService.sendTmplEmail("858869180@qq.com,2294808913@qq.com", msg, RecipientType.TO);
}
/**
* 参数找不到会报错,不可取
*/
@Test
public void tmplGroovyTest(){
Map<String, String> map = new HashMap<>();
map.put("title", "测试邮件标题");
map.put("first", "大写头");
map.put("testMan", "王威");
try {
Template t = TemplateCache.getIns().get("templates/mail/testMail.html");
StringWriter sw = new StringWriter();
t.make(map).writeTo(sw);
System.out.println(sw.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
-
5.配置文件
-
5.1 pom文件
-
<dependency>
<groupId>javax.mail</groupId>
<artifactId>javax.mail-api</artifactId>
<version>1.6.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all -->
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>${groovy.version}</version>
<type>pom</type>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
</dependency>
-
5.2 velocity配置文件
directive.foreach.counter.name = velocityCount
directive.foreach.counter.initial.value = 1
input.encoding=UTF-8
output.encoding=UTF-8
Velocity.ENCODING_DEFAULT=UTF-8
Velocity.INPUT_ENCODING=UTF-8
Velocity.OUTPUT_ENCODING=UTF-8
default.contentType=text/html; charset=UTF-8
#layout
tools.view.servlet.layout.directory=/WEB-INF/view/layout/