本章概要
- 邮件发送
13.1 邮件发送
13.1.1 发送前的准备
此处以 QQ 邮箱为例介绍邮件的发送过程。使用 QQ 邮箱发送邮件,首先要申请开通 POP3/SMTP 服务或者 IMAP/SMTP 服务。
SMTP 服务全称为 Simple Mail Transfer Protocol ,译作简单邮件传输协议,它定义了邮件客户端软件与 SMTP 服务之间,以及 SMTP 服务器与 SMTP 服务器之间的通信规则。
也就是说,aaa@qq.com 用户先将邮件投递到腾讯的 SMTP 服务器,这个过程就使用了 SMTP 协议,然后腾讯的 SMTP 服务器将邮件投递到网易的 SMTP 服务器,这个过程依然使用了 SMTP 协议,SMTP 服务器就是用来接收邮件的。
而 POP3 全称为 Post Office Protocol 3,译作邮局协议,它定义了邮件客户端与 POP3 服务器之间的通信规则。当邮件到达网易的 SMTP 服务器之后,111@163.com 用户需要登录服务器查看邮件,这个时候就用上该协议了:邮件服务商会为每一个用户提供专门的邮件存储空间,SMTP 服务器收到邮件之后,将邮件保存到相应的邮件存储空间中,如果用户要读取邮件,就需要通过邮件服务商的 POP3 邮件服务器来完成。
至于 IMAP 协议,则是对 POP3 协议的扩展,功能更强,作用类似。下面介绍 QQ 邮箱开通 POP3/SMTP 服务或者 IMAP/SMTP 服务的步骤。
步骤01:登录 QQ 邮箱,依次单击顶部的设置按钮和账户按钮
步骤02:在账户选项卡下发找到 POP3/SMTP 服务,单击后方的“开启”按钮
单击开启按钮后,按照引导步骤发送短信,操作成功后,会获取一个授权码,将授权码保存下来过后使用
13.1.2 发送
1. 环境搭建
创建 Spring Boot 项目,添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在application.properties 中完成邮件基本信息配置
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username=邮箱地址(aaa@qq.com)
spring.mail.password=13.1.1申请到的授权码
spring.mail.default-encoding=UTF-8
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.debug=true
这里配置了邮件服务器的地址、端口(可以是465或者587)、用户的帐号和密码以及默认编码、SSL 连接配置等,最后开启 debug ,这样方便开发者查看邮件发送日志。注意,SSL的配置可以在QQ邮箱帮助中心看到相关文档,如下
3. 发送简单邮件
创建一个 MailService 用来封装邮件的发送
@Component
public class MailService {
@Autowired
JavaMailSender javaMailSender;
public void sendSimpleMail(String from, String to, String cc, String subject, String content) {
SimpleMailMessage simpMsg = new SimpleMailMessage();
simpMsg.setFrom(from);
simpMsg.setTo(to);
simpMsg.setCc(cc);
simpMsg.setSubject(subject);
simpMsg.setText(content);
javaMailSender.send(simpMsg);
}
}
代码解释:
- JavaMailSender 是 Spring Boot 在 MailSenderPropertiesConfiguration 类中配置好的,该类在 Mail 自动配置类 MailSenderAutoConfiguration 中导入,因此这里注入 JavaMailSender 就可以使用了
- sendSimpleMail 方法的5个参数分别表示邮件发送者、收件人、抄送人、邮件主题以及邮件内容
- 简单邮件可以直接构建一个 SimpleMailMessage 对象进行配置,配置完成后,通过 JavaMailSender 将邮件发送出去
配置完成后,在单元测试中写一个测试方法进行测试。
@RunWith(SpringRunner.class)
@SpringBootTest
public class SendmailApplicationTests {
@Autowired
MailService mailService;
@Test
public void sendSimpleMail() {
mailService.sendSimpleMail("aaa@qq.com",
"bbb@qq.com",
"ccc@qq.com",
"Spring Boot 测试邮件发送-主题",
"测试邮件内容");
}
}
执行该方法,查看结果(注意查看垃圾箱)
3. 发送带附件的邮件
通过调用 Attachment 方法即可添加附件,该方法调用多次即可添加多个附件。在 MailService 中添加如下方法
public void sendAttachFileMail(String from, String to,String subject, String content, File file) {
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content);
helper.addAttachment(file.getName(), file);
javaMailSender.send(message);
} catch (MessagingException e) {
e.printStackTrace();
}
}
这里使用 MimeMessageHelper 简化了邮件配置,他的构造方法的第二个参数 true 表示构造一个 multipart message 类型的邮件,此类邮件包含多个正文、附件以及内嵌资源,邮件的表现形式更加丰富。最后通过 addAttachment 方法添加附件。
在单元测试中添加如下方法进行测试:
@Test
public void sendAttachmentFileMail(){
mailService.sendAttachFileMail("aaa@qq.com",
"bbb@qq.com",
"Spring Boot 测试附件邮件发送-主题",
"测试附件邮件内容",
new File("F:\\测试文档\\Java代码编写及安全规范.docx"));
}
执行单元测试方法,邮件发送结果如下
4. 发送带图片资源的邮件
有点邮件正文可能要插入图片,使用 FileSystemResource 可以实现这一功能(用 Attachment 方法也可以,把图片当成附件发送),在 MailService 中添加如下方法
public void sendMailWithImg(String from, String to, String subject, String content, String[] srcPath, String[] resIds) {
if (srcPath.length != resIds.length) {
System.out.println("发送失败");
return;
}
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper =
new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
for (int i = 0; i < srcPath.length; i++) {
FileSystemResource res =
new FileSystemResource(new File(srcPath[i]));
helper.addInline(resIds[i], res);
}
javaMailSender.send(message);
} catch (MessagingException e) {
System.out.println("发送失败");
}
}
在发送邮件时分别传入图片资源路径和资源id,通过 FileSystemResource 构造静态资源,然后调用 addInline 方法将资源加入邮件对象中。
注意,在调用 MimeMessageHelper 中的 setText 方法时,第二个参数 true 表示邮件正文是 HTML 格式的,该参数不传默认为 false。
接下来在测试类中添加如下方法进行测试:
@Test
public void sendMailWithImg() {
mailService.sendMailWithImg("aaa@qq.com",
"bbb@qq.com",
"Spring Boot 测试邮件主题(图片)",
"<div>hello,这是一封带图片资源的邮件:" +
"这是图片1:<div><img src='cid:p01'/></div>" +
"这是图片2:<div><img src='cid:p02'/></div>" +
"</div>",
new String[]{"C:\\Users\\dell\\Pictures\\我的自拍1.jpg",
"C:\\Users\\dell\\Pictures\\我的自拍2.jpg"},
new String[]{"p01", "p02"});
}
邮件的正文是一段 HTML 文本,用 cid 标注出两个静态资源,分别为 p01 和 p02,邮件发送结果如下
5. 使用 FreeMarker 构建邮件模板
对于格式复杂的邮件,如果采用字符串进行 HTML 拼接,不但容易出错,而且不易于维护,使用 HTML 模板可以很好的解决这一问题,加入 FreeMarker 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
然后在 MailService 添加如下方法:
public void sendHtmlMail(String from, String to, String subject, String content) {
try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo(to);
helper.setFrom(from);
helper.setSubject(subject);
helper.setText(content, true);
javaMailSender.send(message);
} catch (MessagingException e) {
System.out.println("发送失败");
}
}
接下来在 resources 目录下创建 ftl 目录作为模板存放位置,在该目录下创建 mailtemplate.ftl 作为邮件模板,内容如下:
<div>邮箱激活</div>
<div>您的注册信息是:
<table border="1">
<tr>
<td>用户名</td>
<td>${username}</td>
</tr>
<tr>
<td>用户性别</td>
<td>${gender}</td>
</tr>
</table>
</div>
<div>
<a href="http://www.baidu.com">核对无误请点击本链接激活邮箱</a>
</div>
再创建一个简单的 User 实体类
public class User {
private String username;
private String gender;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
最后在单元测试类中添加如下方法进行测试
@Test
public void sendHtmlMail2() {
try {
Configuration configuration = new Configuration(Configuration.VERSION_2_3_0);
ClassLoader loader = SendmailApplication.class.getClassLoader();
configuration.setClassLoaderForTemplateLoading(loader,"ftl");
Template template = configuration.getTemplate("mailtemplate.ftl");
StringWriter mail = new StringWriter();
User user = new User();
user.setGender("男");
user.setUsername("胡歌");
template.process(user, mail);
mailService.sendHtmlMail("aaa@qq.com",
"bbb@qq.com",
"Spring Boot 测试邮件主题(FreeMarker)",
mail.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
首先配置 FreeMarker 模板位置,配置模板文件,然后结合 User 对象渲染模板,将渲染结果发送出去,执行该方法,邮件发送结果如下:
6. 使用 Thymeleaf 构建邮件模板
既然可以使用 FreeMarker 构建,当然也可以使用 Thymeleaf构建邮件模板,使用 Thymeleaf 构建邮件模板相对来说更加方便。添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Thymeleaf 邮件模板默认位置是在 resources/templates 目录下,创建相应的目录,然后创建邮件模板 mailtemplate.html ,代码如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>邮件</title>
</head>
<body>
<div>邮箱激活</div>
<div>您的注册信息是:
<table border="1">
<tr>
<td>用户名</td>
<td th:text="${username}"></td>
</tr>
<tr>
<td>用户性别</td>
<td th:text="${gender}"></td>
</tr>
</table>
</div>
<div>
<a href="http://www.baidu.com">核对无误请点击本链接激活邮箱</a>
</div>
</body>
</html>
然后在单元测试类中添加如下代码进行测试
@Autowired
TemplateEngine templateEngine;
@Test
public void sendHtmlMailThymeleaf() {
Context ctx = new Context();
ctx.setVariable("username", "胡歌");
ctx.setVariable("gender", "男");
String mail = templateEngine.process("mailtemplate.html", ctx);
mailService.sendHtmlMail("aaa@qq.com",
"bbb@qq.com",
"Spring Boot 测试邮件主题(Thymeleaf)",
mail);
}
不同于 FreeMarker ,Thymeleaf 提供了 TemplateEngine 来对模板进行渲染,通过 Context 构造模板中变量需要的值,这种方式比 FreeMarker 构建模板更加方便。最后执行该测试方法,结果如下
这几种不同的邮件发送方式基本上能满足大部分的业务需求,可以在实际开发中合理选择。