目录
一、背景
邮件在工作中中经常被用到,场景非常的广泛,比如:注册账户,接收验证码,或者是公司发的公告,又或者是异常消息推送等等。本文中的Spring Boot 的版本是2.6.0
二、maven依赖
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.alian</groupId>
<artifactId>springEmail</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringEmail</name>
<description>SpringEmail</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
三、编码实现
3.1、邮件对象
Email.java
@Data
public class Email {
/**
* 邮件发送方
*/
private String sender;
/**
* 邮件接收方
*/
private List<String> receiverList = new ArrayList<>();
/**
* 邮件抄送方
*/
private List<String> ccList = new ArrayList<>();
/**
* 邮件主题
*/
private String subject;
/**
* 邮件内容
*/
private String content;
/**
* 嵌入式资源(key:contentId,value:filePath)
*/
private Map<String, String> inlineMap=new HashMap<>();
/**
* 附件列表(value:filePath)
*/
private List<String> attachmentList = new ArrayList<>();
public String[] getReceiverArray() {
return this.receiverList.toArray(new String[0]);
}
public String[] getCcArray() {
return this.ccList.toArray(new String[0]);
}
public String toJsonString() {
return JSON.toJSONString(this);
}
}
邮件对象主要包含:
- 邮件发送方(sender)
- 邮件接收方(receiverList),支持发送多人
- 邮件抄送方(ccList),支持抄送多人
- 邮件主题(subject)
- 邮件内容(content),可以使用freeMark模板
- 嵌入式资源(inlineMap),支持嵌入多个资源
- 附件列表(attachmentList),支持多个附件发送
3.2、服务层
EmailService.java
@Slf4j
@Service
public class EmailService {
@Autowired
private JavaMailSenderImpl mailSender;
@Autowired
private FreeMarkerConfigurer configurer;
public boolean sendSimpleEmail(Email email) {
log.info("发送简单邮件信息:{}", email.toJsonString());
// 实例化SimpleMailMessage
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
try {
// 设置邮件发件人
simpleMailMessage.setFrom(email.getSender());
// 设置邮件接收人(可以多人)
simpleMailMessage.setTo(email.getReceiverArray());
// 设置邮件抄送
String[] ccArray = email.getCcArray();
if (ccArray.length > 0) {
simpleMailMessage.setCc(ccArray); // 普通抄送[邮件接收人可以看到抄送给谁了]
// simpleMailMessage.setBcc(); // 盲抄送[邮件接收人可以看不到抄送给谁了]
}
// 设置邮件主题
simpleMailMessage.setSubject(email.getSubject());
// 设置邮件内容
simpleMailMessage.setText(email.getContent());
// 发送简单邮件
mailSender.send(simpleMailMessage);
return true;
} catch (Exception e) {
log.error(null, e);
}
return false;
}
public boolean sendComplexEmail(Email email, String templateName, Map<String, Object> data) {
log.info("发送复杂邮件信息:{}", email.toJsonString());
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true,"UTF-8");
// 设置邮件发件人
messageHelper.setFrom(email.getSender());
// 设置邮件接收人(可以多人)
messageHelper.setTo(email.getReceiverArray());
// 设置邮件抄送
String[] ccArray = email.getCcArray();
if (ccArray.length > 0) {
messageHelper.setCc(ccArray); // 普通抄送[邮件接收人可以看到抄送给谁了]
// messageHelper.setBcc(ccArray); // 盲抄送[邮件接收人可以看不到抄送给谁了]
}
// 设置邮件主题
messageHelper.setSubject(email.getSubject());
// 通过模板后去内容
String content = getContent(templateName, data);
// 设置邮件内容,第二个参数为true表示将发送内容设置为"text/html"
messageHelper.setText(content, true);
// 批量处理嵌入式资源
Map<String, String> inlineMap = email.getInlineMap();
for (Map.Entry<String, String> entry : inlineMap.entrySet()) {
String contentId = entry.getKey();
String filePath = entry.getValue();
// 设置嵌入式资源
messageHelper.addInline(contentId, new File(filePath));
}
// 批量处理附件
List<String> attachmentList = email.getAttachmentList();
for (int i = 0; i < attachmentList.size(); i++) {
String filePath = attachmentList.get(i);
String fileName = i + "-" + filePath.substring(filePath.lastIndexOf(File.separator));
// 设置附件
messageHelper.addAttachment(fileName, new File(filePath));
}
// 发送简单邮件
mailSender.send(mimeMessage);
return true;
} catch (Exception e) {
log.error(null, e);
}
return false;
}
public String getContent(String templateName, Map<String, Object> data) throws TemplateException, IOException {
// 获取模板
Template template = configurer.getConfiguration().getTemplate(templateName, "UTF-8");
// 填充数据并把模板转为字符串
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
}
}
其中:
- SimpleMailMessage:用来发送简单的文本邮件
- MimeMessage:用来发送html邮件、带附件的邮件、有静态资源(图片)的邮件
如果你的代码里有多个附件或者多嵌入式资源,就需要如下:
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage,true,"UTF-8");
第二个参数说明你有多个资源文件(嵌入式资源或者附件)
3.2.1、抄送人
- 普通抄送 setCc():邮件接收人可以看到抄送给谁了
- 盲抄送 setBcc():邮件接收人可以看不到抄送给谁了
3.2.2、嵌入式资源
通过MimeMessageHelper的addInline方法可以添加嵌入式资源,比如在邮件里显示图片。
// 设置嵌入式资源
messageHelper.addInline(contentId, new File(filePath));
需要注意的是
- 如果模板是<img src=‘cid:CSDN1234’/>,那么contentId就是CSDN1234不要把cid:一起写入了
- 有多个资源时,contentId不要重复了
3.2.3、附件
通过MimeMessageHelper的addAttachment方法可以添加附件。
// 设置附件
messageHelper.addAttachment(fileName, new File(filePath));
需要注意的是:这里的文件名fileName尽量不要相同,如果有相同的,最好加以区分
3.3、邮件模板
complex-email.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
</head>
<body>
<p>
<img width="100" height="50" src='cid:CSDN1234'/>
<img width="100" height="100"src='cid:Alian1223'/>
</br>
<span>欢迎使用CSDN博客系统</span>
</p>
<table>
<tr>
<td>平台地址:</td>
<td><a href="${website}">${website}</a></td>
</tr>
<tr>
<td>登录账号:</td>
<td>${account}</td>
</tr>
<tr>
<td>登录密码:</td>
<td>${password}</td>
</tr>
</table>
<p>
温馨提示:密码有效期为90天,密码必须包含大小写字母数字及特殊符号且长度不小于8位,请妥善保管好密码,祝您工作顺利!
</p>
</body>
</html>
需要注意的是:我这里的模板文件complex-email.ftl放到resources目录下了
3.3.1、模板引擎
FreeMarker是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。因为我们发送邮件可能会有很多种,频繁改动代码不方便,所以我们采用模板,增加一个模板,然后把参数传入即可。
@Autowired
private FreeMarkerConfigurer configurer;
public String getContent(String templateName, Map<String, Object> data) throws TemplateException, IOException {
// 获取模板
Template template = configurer.getConfiguration().getTemplate(templateName, "UTF-8");
// 填充数据并把模板转为字符串
return FreeMarkerTemplateUtils.processTemplateIntoString(template, data);
}
我们通过FreeMarkerConfigurer来获取到我们的模板,然后通过Spring提供的方法FreeMarkerTemplateUtils.processTemplateIntoString(template, data)完成数据的封装。
当我们使用MimeMessage发送html邮件时,设置邮件内容时,第二个参数为true表示将发送内容设置为"text/html",不然就会把内容当成文本发送了。
// 设置邮件内容,第二个参数为true表示将发送内容设置为"text/html"
messageHelper.setText(content, true);
3.4、配置文件
application.properties
#服务端口
server.port=8080
#上下文路径
server.servlet.context-path=/email
#163邮箱
#配置邮件发送的服务器
spring.mail.host=smtp.163.com
#邮件的发送者
spring.mail.username=你的163邮箱
#邮箱申请的授权码
spring.mail.password=你的授权码
#邮件的编码格式
spring.mail.default-encoding=UTF-8
#邮件服务器连接超时
spring.mail.properties.mail.smtp.connectiontimeout=10000
#邮件接收超时
spring.mail.properties.mail.smtp.timeout=20000
#邮件发送超时
spring.mail.properties.mail.smtp.writetimeout=10000
我上面是使用163邮箱发送,如果你想用QQ邮箱或者企业微信邮箱则类似如下配置:
QQ邮箱
##qq邮箱配置(开启POP3,设置客户端授权码)
##配置邮件发送的服务器
#spring.mail.host=smtp.qq.com
##邮件的发送者
#spring.mail.username=你的QQ号@qq.com
##邮箱申请的授权码
#spring.mail.password=你的授权码
企业微信邮箱
##企业微信邮箱
##配置邮件发送的服务器
#spring.mail.host=smtp.exmail.qq.com
##邮件的发送者
#spring.mail.username=你的企业微信邮箱
##邮箱申请的授权码
#spring.mail.password=你的授权码
- 上面的password不是邮箱登录密码,是开启POP3之后设置的客户端授权码
- 默认端口2525,当我们使用465端口时,新增如下配置:
spring.mail.port=465
spring.mail.properties.mail.smtp.ssl.enable=true
四、测试
4.1、发送简单邮件
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class SimpleEmailTest {
@Autowired
private EmailService emailService;
@Test
public void sendSimpleEmail() {
List<String> receiverList = new ArrayList<>();
receiverList.add("zhanglian@wtsd.cn");
Email email = new Email();
email.setSender("wtsd_ywdl@163.com");
email.setReceiverList(receiverList);
email.setSubject("发送简单邮件测试(纯文本)");
email.setContent("文章你都没有看完,好意思说有5年Java功力???");
boolean b = emailService.sendSimpleEmail(email);
log.info("发送结果:{}", b);
}
}
4.2、发送复杂邮件
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class ComplexEmailTest {
@Autowired
private EmailService emailService;
@Test
public void sendComplexEmail() {
List<String> receiverList = new ArrayList<>();
receiverList.add("zhanglian@wtsd.cn");
// 邮件对象
Email email = new Email();
email.setSender("wtsd_ywdl@163.com");
email.setReceiverList(receiverList);
email.setSubject("发送复杂邮件测试(带附件)");
// 嵌入式资源
Map<String, String> inlineMap = email.getInlineMap();
inlineMap.put("CSDN1234", "C:\\myFile\\CSDN\\csdn.png");
inlineMap.put("Alian1223", "C:\\myFile\\CSDN\\alian.png");
email.setInlineMap(inlineMap);
// 附件
List<String> attachmentList = email.getAttachmentList();
attachmentList.add("C:\\myFile\\CSDN\\csdn.png");
attachmentList.add("C:\\myFile\\CSDN\\alian.png");
email.setAttachmentList(attachmentList);
// 设置模板数据
Map<String, Object> data = new HashMap<>();
data.put("title", "注册成功");
data.put("website", "https://blog.csdn.net/Alian_1223");
data.put("account", "alian");
data.put("password", "Alian1234");
boolean b = emailService.sendComplexEmail(email, "complex-email.ftl", data);
log.info("发送结果:{}", b);
}
}