目录
邮件发送是一个非常非常常见的功能,注册时的身份认证、 重要通知发送等都会用到邮件发送。Sun公司提供了JavaMail用来实现邮件发送,但是配置烦琐。Spring 中提供了JavaMailSender用来简化邮件配置。Spring Boot 则提供了 MailSenderAutoConfiguration 对邮件的发送做了进一步简化现在我们就来学习在Spring Boot中如何发送邮件。
本文将介绍四种类型邮件的发送方式:简单文本邮件、带附件邮件、邮件正文带图片、使用Themeleaf构建邮件模板。
一、开启SMTP服务
如果想要发送邮件,我们需要开启SMTP服务,这里以QQ邮箱为例:
设置--账户
拉到下面,找到账户安全,如下图所示,需要做的就是开启SMTP服务,然后生成授权码,代码中发邮件的时候需要用到授权码:
二、搭建SpringBoot项目
完整的项目已上传到gitee:https://gitee.com/tt1996/AllOpenDemo.git
首先是pom.xml文件,重点是需要添加,spring-boot-starter-mail依赖,完整的pom文件如下:
<?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 http://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.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>springboot_mail</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<knife4j.version>2.0.4</knife4j.version>
<lombok.version>1.18.10</lombok.version>
<hutool.version>5.2.5</hutool.version>
<mybatis-plus.version>3.0.6</mybatis-plus.version>
</properties>
<dependencies>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--junit依赖-->
<!-- <dependency>-->
<!-- <groupId>junit</groupId>-->
<!-- <artifactId>junit</artifactId>-->
<!-- <version>4.8.2</version>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
<!--spring test测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
</project>
然后是application.yml配置文件。其中用户名username和口令一定要换成你自己的,口令就是第一步开启SMTP服务发给你的授权码。
关于配置的含义,注释已经详细做了介绍。完整配置如下:
server:
port: 8086
#邮件设置
#spring.mail.host=smtp.exmail.qq.com
#暂时改为个人邮箱smtp服务器进行测试
spring:
mail:
#坑爹的地方:host 通道个人邮箱和企业邮箱通道不同。163的个人邮箱:smtp.163.com ,企业邮箱:smtp.qiye.163.com
# 腾讯的,个人smtp.qq.com, 企业的:smtp.exmail.qq.com
host: smtp.qq.com
username: xxx@qq.com
# 口令是QQ邮箱开通的smtp服务后得到的客户端授权码,不是你的邮箱登录密码
password: xxxxx
default-encoding: UTF-8
properties:
mail:
smtp:
auth: true
socketFactory:
class: javax.net.ssl.SSLSocketFactory
port: 465
# 开启debug,方便查看邮件发送日志
debug: true
项目目录结构图如下:
三、发送邮件
首先定义接口MailService.java,如下所示。接口中定义了四个方法,分别是用来发送简单文本邮件、带附件邮件、带图片的邮件、使用Themeleaf构建邮件模板的。
package com.ztt.service;
import java.io.File;
import java.util.List;
public interface MailService {
void sendSimpleMail(String mailFrom, String mailFromNick, String mailTo, String cc, String subject, String content);
void sendMailWithAttachments(String mailFrom, String mailFromNick, String mailTo, String cc, String subject, String content,
List<File> files);
void sendMailWithImage(String mailFrom, String mailFromNick, String mailTo, String cc, String subject, String content,
String[] imagePaths, String[] imageId);
void sendHtmlMailThymeLeaf(String mailFrom, String mailFromNick, String mailTo, String cc, String subject, String content);
}
然后,是接口的实现类MailServiceImpl.java
每个方法的功能,及关键代码都有注释。
package com.ztt.service.impl;
import com.ztt.service.MailService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.List;
@Service("mailService")
public class MailServiceImpl implements MailService {
private static final Logger logger = LoggerFactory.getLogger(MailServiceImpl.class);
/**
* JavaMailSender是Spring Boot在MailSenderPropertiesConfiguration 类中配直好的,该类在 Mail
* 自动配置类 MailSenderAutoConfiguration 中导入 因此这里注入 JavaMailSender 就可以使用了
*/
@Autowired
private JavaMailSender mailSender;
/**
* 1、发送普通文本邮件
*
* @param mailFrom 发件人邮箱
* @param mailFromNick 发件人昵称
* @param mailTo 收件人邮箱
* @param cc 抄送人邮箱(可为空,方法内部处理)
* @param subject 主题
* @param content 内容
*/
@Override
public void sendSimpleMail(String mailFrom, String mailFromNick, String mailTo, String cc,
String subject, String content) {
try {
// 多个收件人之间用英文逗号分隔
String[] mailToArr = mailTo.split(",");
for (String address : mailToArr) {
// 简单邮件信息类
MimeMessage mimeMessage = mailSender.createMimeMessage();
// HTML邮件信息类
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
// 昵称
mimeMessageHelper.setFrom(new InternetAddress(mailFromNick + " <" + mailFrom + ">"));
mimeMessageHelper.setTo(address);
if (!StringUtils.isEmpty(cc)) {
mimeMessageHelper.setCc(cc);
}
mimeMessageHelper.setSubject(subject);
mimeMessageHelper.setText(content);
mailSender.send(mimeMessage);
}
} catch (Exception e) {
logger.error("发送邮件失败:", e);
}
}
/**
* 2、发送带附件的邮件
*
* @param mailFrom 发件人
* @param mailFromNick 发件人昵称
* @param mailTo 收件人
* @param cc 抄送人
* @param subject
* @param content
* @param files
*/
@Override
public void sendMailWithAttachments(String mailFrom, String mailFromNick, String mailTo, String cc,
String subject, String content, List<File> files) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
/*
第二个参数true表示构造一个multipart message类型的邮件,multipart message类型的邮件包含多个正文、附件以及内嵌资源,
邮件的表现形式更丰富
*/
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(new InternetAddress(mailFromNick + " <" + mailFrom + ">"));
mimeMessageHelper.setSubject(subject);
mimeMessageHelper.setText(content);
// 设置多个收件人
String[] toAddress = mailTo.split(",");
mimeMessageHelper.setTo(toAddress);
if (!StringUtils.isEmpty(cc)) {
mimeMessageHelper.setCc(cc);
}
// 添加附件
if (null != files) {
for (File file : files) {
// 通过addAttachment方法添加附件
mimeMessageHelper.addAttachment(file.getName(), file);
}
}
} catch (MessagingException e) {
e.printStackTrace();
}
//发送邮件
mailSender.send(mimeMessage);
}
/**
* 3、正文内容带图片
*
* @param mailFrom
* @param mailFromNick
* @param mailTo
* @param cc 抄送人
* @param subject
* @param content
* @param imagePaths
* @param imageId
*/
@Override
public void sendMailWithImage(String mailFrom, String mailFromNick, String mailTo, String cc, String subject,
String content, String[] imagePaths, String[] imageId) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(new InternetAddress(mailFromNick + " <" + mailFrom + ">"));
// 设置多个收件人
String[] toAddress = mailTo.split(",");
mimeMessageHelper.setTo(toAddress);
if (!StringUtils.isEmpty(cc)) {
mimeMessageHelper.setCc(cc);
}
mimeMessageHelper.setSubject(subject);
// 第二个参数为true表示邮件正文是html格式的,默认是false
mimeMessageHelper.setText(content, true);
// 添加图片
if (imagePaths != null && imagePaths.length != 0) {
for (int i = 0; i < imagePaths.length; i++) {
// 通过FileSystemResource构造静态资源
FileSystemResource fileSystemResource = new FileSystemResource(imagePaths[i]);
// 调用addInline方法将资源加入邮件对象中
mimeMessageHelper.addInline(imageId[i], fileSystemResource);
}
}
mailSender.send(mimeMessage);
} catch (MessagingException e) {
System.out.println(e);
}
}
/**
* 4、使用Themeleaf构建邮件模板。需额外加spring-boot-starter-thymeleaf依赖
*
* @param mailFrom
* @param mailFromNick
* @param mailTo
* @param cc
* @param subject
* @param content
*/
@Override
public void sendHtmlMailThymeLeaf(String mailFrom, String mailFromNick, String mailTo, String cc,
String subject, String content) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setFrom(new InternetAddress(mailFromNick + " <" + mailFrom + ">"));
// 设置多个收件人
String[] toAddress = mailTo.split(",");
mimeMessageHelper.setTo(toAddress);
if (!StringUtils.isEmpty(cc)) {
mimeMessageHelper.setCc(cc);
}
mimeMessageHelper.setSubject(subject);
// 第二个参数为true表示邮件正文是html格式的,默认是false
mimeMessageHelper.setText(content, true);
mailSender.send(mimeMessage);
} catch (MessagingException e) {
System.out.println(e);
}
}
}
然后是测试类SendmailApplicationTest.java:
为了方便测试,我针对每个方法都单独写了个测试方法。只需要把相关邮箱换成自己的,相关文件图片换成自己的,就可以直接运行测试看结果的。每个方法都可单独直接运行,不要从Starter启动类启动。
package com.ztt.controller;
import com.ztt.service.MailService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SendmailApplicationTest {
@Autowired
private MailService mailService;
// 发件人要跟yml配置文件里填写的邮箱一致
String mailFrom = "xx@qq.com";
// 收件人
String mailTo = "xx@newhope.cn";
// 抄送
String cc = "xx@163.com";
/**
* 1、测试普通邮件发送
*/
@Test
public void testSendSimpleMail() {
String result = "发送邮件成功";
try {
mailService.sendSimpleMail(mailFrom, "一个大帅哥", mailTo, cc, "TestMail", "Hello World !");
} catch (Exception e) {
result = "发送邮件失败!";
System.out.println(result);
System.out.println(e);
}
System.out.println(result);
}
/**
* 2、测试带附件的方法
*/
@Test
public void testSendAttachment() {
File imgFile = new File("src\\main\\java\\com\\ztt\\controller\\f1bdd00e8c.jpg");
File txtFile = new File("src\\main\\java\\com\\ztt\\controller\\hello.txt");
List<File> fileList = new ArrayList<>();
fileList.add(imgFile);
fileList.add(txtFile);
// 发件人要跟yml配置文件里填写的邮箱一致
String mailFrom = "1798206908@qq.com";
String mailTo = "zhaott@newhope.cn";
String result = "发送邮件成功";
try {
mailService.sendMailWithAttachments(mailFrom, "一个大帅哥", mailTo, cc, "TestMail", "Hello World !", fileList);
} catch (Exception e) {
result = "发送邮件失败!";
System.out.println(result);
System.out.println(e);
}
System.out.println(result);
}
/**
* 3、正文带图片
*/
@Test
public void testSendMailWithImage() {
// 图片路径
String image01Path = "E:\\personal\\gittest\\学习项目库\\learning_project_library\\SpringBoot_mail\\src\\main\\java\\com\\ztt\\controller\\2ed0c0d5a2.jpg";
String image02Path = "E:\\personal\\gittest\\学习项目库\\learning_project_library\\SpringBoot_mail\\src\\main\\java\\com\\ztt\\controller\\3bcd0b6866.jpg";
String[] imageArr = new String[]{image01Path, image02Path};
String[] imageIdArr = new String[]{"image01", "image02"};
String result = "发送邮件成功";
try {
String contentHtml = "这是图片1:<div><img src='cid:image01'/></div>" +
"这是图片2:<div><img src='cid:image02'/></div>";
mailService.sendMailWithImage(mailFrom, "一个大帅哥", mailTo, cc, "TestMail", contentHtml, imageArr, imageIdArr);
} catch (Exception e) {
result = "发送邮件失败!";
System.out.println(result);
System.out.println(e);
}
System.out.println(result);
}
/**
* 4、使用ThymeLeaf
*/
// 注入TemplateEngine
@Autowired
TemplateEngine templateEngine;
@Test
public void testSendHtmlMailThymeLeaf() {
// 注意导入的包是org.thymeleaf.context
Context context = new Context();
context.setVariable("username", "比尔盖茨");
context.setVariable("age", "18");
String content = templateEngine.process("mailTemplate01.html", context);
mailService.sendHtmlMailThymeLeaf(mailFrom, "一个大帅哥", mailTo, cc, "TestMail", content);
System.out.println("邮件发送成功");
}
}
发送普通文本邮件和带附件的邮件比较简单。这里就不作介绍了。
首先说一下第三种。。
sendMailWithImage(String mailFrom, String mailFromNick, String mailTo, String cc, String subject,
String content, String[] imagePaths, String[] imageId)
这个方法实现了正文带图片的功能。不同于前两种的是,String content是html格式的文本,里面用cid标注静态资源(本文是src='cid:image01'),String[] imagePaths存储的是图片的路径,String[] imageId存储了每张图片的编号,这个编号是可以自己随便定义的,但是必须跟content里面使用的cid名称一致。
然后说一下第四种。
对于格式复杂的邮件,如果采用字符串进行html拼接,不但容易出错,而且不易于维护,使用html模板可以很好的解决这一问题。模板可以选择FreeMaker或者ThymeLeaf,使用Thymeleaf构建邮件模板,更加方便。本文使用的也是Thymeleaf。使用Thymeleaf,首先需要添加Thymeleaf的依赖,参考开头的pom文件。
Thymeleaf邮件模板默认位置是在resources/templates目录下,创建相应的目录,然后创建邮件模板,mailTemplate01.html。如下所示:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>
通过ThymeLeaf发送html邮件
</h2>
<table border="1">
<tr>
<td>用户名</td>
<!--使用th,html标签内要导入xmlns:th="http://www.thymeleaf.org"-->
<td th:text="${username}"></td>
</tr>
<tr>
<td>年龄</td>
<td th:text="${age}"></td>
</tr>
</table>
<div>
这是一张图片:
</div>
<div>
<img src="E:\personal\gittest\学习项目库\learning_project_library\SpringBoot_mail\src\main\java\com\ztt\controller\2ed0c0d5a2.jpg"/>
</div>
</body>
</html>
这个模板是我随便写的,只定义了一个表格,然后放了张图片进去。
效果如下: