Spring Boot 发送简单邮件、HTML5邮件、图片邮件、带附件的邮件以及thymeleaf模版邮件
spring-boot-starter-mail
- 网站发送邮件最早使用 JavaMail相关 API 来写发送邮件的相关代码,后来 Spring 推出了 JavaMailSender 简化了邮件发送代码的编写。现在Spring Boot 在 JavaMailSender 的基础上又进行了封装,就有了现在的 spring-boot-starter-mail
- Spring 的 JavaMailSenderImpl 提供了强大的邮件发送功能,可发送普通文本邮件、带附件邮件、HTML格式邮件、带图片邮件,设置发送内容编码格式、设置发送人的显示名称
JavaMail API 按功能可分为如下三大类:
- Message 类 :创建和解析邮件的核心 API,用于创建一封邮件,可以设置发件人、收件人、邮件主题、正文信息、发送时间等信息
- Transport 类:发送邮件的核心 API 类
- Store 类:接收邮件的核心API类
邮件的相关协议
- SMTP 协议:发送邮件协议
- POP3 协议:获取邮件协议;
- IMAP:接收信息的高级协议;
- MIME:邮件拓展内容格式:信息格式,附件格式
图示互相发送邮件的过程
初始化项目
我演示的是QQ邮箱,所以先把QQ邮箱的SMTP和POP3服务等开起来
记住这个授权码
项目结构
pom.xml导入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在application.properties做如下配置,需要将 spring.mail.username 和 spring.mail.password 改成自己邮箱对应的登录名和是开启 POP3 之后设置的客户端授权密码
spring.mail.host=smtp.qq.com
spring.mail.username=你的QQ邮箱号@qq.com
spring.mail.password=刚刚的授权码
spring.mail.default-encoding=UTF-8
MailService.java
package wen.mail.service;
public interface MailService {
public void sendSimpleMail(String to, String subject, String content);
public void sendHtmlMail(String to, String subject, String content);
public void sendAttachmentsMail(String to, String subject, String content, String filePath);
public void sendInlineResourcesMail(String to, String subject, String content, String srcPath, String srcId);
}
实现类MailServiceImpl.java
package wen.mail.service.impl;
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.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import wen.mail.service.MailService;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
@Component
public class MailServiceImpl implements MailService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String from;
/**
* 发送简单邮件
* @param to
* @param subject
* @param content
*/
@Override
public void sendSimpleMail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(to);
message.setSubject(subject);
message.setText(content);
try {
mailSender.send(message);
logger.info("邮件已发送");
} catch (Exception e) {
logger.error("异常!", e);
}
}
/**
* 发送带 HTML 模板的邮件
* @param to
* @param subject
* @param content
*/
@Override
public void sendHtmlMail(String to, String subject, String content) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
//true 表示创建一个 multipart message
helper.setText(content, true);
mailSender.send(message);
logger.info("html 邮件发送成功!");
} catch (MessagingException e) {
logger.error("HTML 邮件异常!", e);
}
}
/**
* 发送带附件的邮件
* @param to
* @param subject
* @param content
* @param filePath
*/
@Override
public void sendAttachmentsMail(String to, String subject, String content, String filePath) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource file = new FileSystemResource(new File((filePath)));
String fileName = file.getFilename();
helper.addAttachment(fileName, file);
mailSender.send(message);
logger.info("带附件的邮件已发送");
} catch (MessagingException e) {
logger.error("发送附件异常!", e);
}
}
/**
* 发送带静态资源的邮件
* @param to
* @param subject
* @param content
* @param srcPath
* @param srcId
*/
@Override
public void sendInlineResourcesMail(String to, String subject, String content, String srcPath, String srcId) {
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
FileSystemResource res = new FileSystemResource(new File(srcPath));
helper.addInline(srcId, res);
mailSender.send(message);
logger.info("嵌入静态资源的邮件已经发送。");
} catch (MessagingException e) {
logger.error("发送嵌入静态资源的邮件时发生异常!", e);
}
}
}
测试类 MailApplicationTests.java
package wen.mail;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import wen.mail.service.MailService;
@SpringBootTest
class MailApplicationTests {
@Autowired
private MailService mailService;
@Test
public void testSimpleMail() throws Exception {
mailService.sendSimpleMail(" your QQ number @qq.com", "The Simple Mail", "Good afternoon, Stephanie!");
}
@Test
public void testHtmlMail() throws Exception {
String content = "<html>\n" + "<body>\n" + " <h6>it is a mail from html mail form</h6>\n" + "</body>\n" + "</html>";
mailService.sendHtmlMail(" your QQ number @qq.com", "HTML mail", content);
}
@Test
public void sendAttachmentsMail() {
String filepath = "附件的存储路径";
mailService.sendAttachmentsMail(" your QQ number @qq.com", "script", "speech", filepath);
}
@Test
public void sendInlineResourceMail() {
String srcId = "Stephanie";
String content = "<html><body>图片邮件:<img src=\'id:" + srcId + "\' ></body></html>";
String imgPath = "图片在电脑的存储路径 -- 1.jpg";
mailService.sendInlineResourcesMail(" your QQ number @qq.com", "image mail", content, imgPath, srcId);
}
}
发送简单邮件
- from:邮件发送者
- to:邮件接收者
- subject:邮件主题
- content:邮件的主体
发送 HTML 格式邮件
- 发送邮件使用 MimeMessageHelper 类,MimeMessageHelper 支持发送复杂邮件模板,支持文本、附件、HTML、图片等,接下来会继续使用
发送带附件的邮件
- 添加多个附件可以使用条 helper.addAttachment(fileName, file)
- 附件可以是图片、压缩包、Word 等任何文件,但是邮件厂商一般都会对附件大小有限制,太大的附件可以使用网盘上传后,在邮件中给出链接
发送静态资源邮件 - 添加多个图片可以使用多条 和helper.addInline(rscId, res) 来实现
这里我明明发的是图片,但是发送过来之后就变成bin了,但是我下载之后改后缀为 .jpg 就没问题
模版邮件
小声BB:我在原来那个项目下建立模版邮件老师出一些很奇妙的BUG,所以我干脆新建项目
pom.xml 导入以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
在项目的 resources下面 template 建立mail 包,再建立mail.html文件写邮件模版
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>title</title>
</head>
<body>
<h3>Template mail <span style="font-size: 35px" th:text="${username}"></span></h3>
</body>
</html>
application.properties
spring.mail.host=smtp.qq.com
spring.mail.username=your QQ number @qq.com
spring.mail.password=之前的授权码
spring.mail.default-encoding=UTF-8
# thymeleaf配置
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
MailDo.java
package wen.templatemail.domain;
import java.util.Map;
public class MailDo {
private String title;
private String content;
private String email;
private Map<String, Object> attachment;
public MailDo() {
}
public MailDo(String title, String content, String email) {
this.title = title;
this.content = content;
this.email = email;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Map<String, Object> getAttachment() {
return attachment;
}
public void setAttachment(Map<String, Object> attachment) {
this.attachment = attachment;
}
}
MailService.java
package wen.templatemail.service;
import wen.templatemail.domain.MailDo;
public interface MailService {
void sendTemplateMail(MailDo mailDo);
}
MailServiceImpl.java
package wen.templatemail.service.impl;
import wen.templatemail.domain.MailDo;
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.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import wen.templatemail.service.MailService;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
@Service
public class MailServiceImpl implements MailService {
private final static Logger log = LoggerFactory.getLogger(MailServiceImpl.class);
@Autowired
private TemplateEngine templateEngine;
@Autowired
private JavaMailSender javaMailSender;
@Value("${spring.mail.username}")
private String from;
@Async
@Override
public void sendTemplateMail(MailDo mailDo) {
try {
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true);
messageHelper.setFrom(from);
messageHelper.setTo(mailDo.getEmail());
messageHelper.setSubject(mailDo.getTitle());
Context context = new Context();
context.setVariables(mailDo.getAttachment());
String emailContent = templateEngine.process("/mail/mail", context);
messageHelper.setText(emailContent, true);
javaMailSender.send(mimeMessage);
} catch (MessagingException e) {
log.error("模板邮件发送失败->message:{}", e.getMessage());
}
}
}
MailController.java
package wen.templatemail.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import wen.templatemail.domain.MailDo;
import wen.templatemail.service.MailService;
import java.util.HashMap;
import java.util.Map;
@Controller
public class MailController {
@Autowired
private MailService mailService;
@GetMapping("/templateMail")
public void mail() {
try {
Map<String, Object> map = new HashMap<>();
map.put("username", "test");
MailDo mailDo = new MailDo("template mail", "", "820352756@qq.com");
mailDo.setAttachment(map);
mailService.sendTemplateMail(mailDo);
} catch (Exception e) {
e.printStackTrace();
}
}
}
开启项目 TemplatemailApplication 浏览器访问 http://localhost:8080/templateMail
运行结果:
邮件发送失败:
- 因为各种原因,总会有邮件发送失败的情况,如邮件发送过于频繁、网络异常等。在出现这种情况的时候我们一般会考虑重新重试发送邮件,会分为以下几个步骤来实现:
- 接收到发送邮件请求,首先记录请求并且入库调用邮件发送接口发送邮件,并且将发送结果记录入库。
- 启动定时系统扫描时间段内,未发送成功并且重试次数小于 3 次的邮件,进行再次发送
异步发送
- 很多时候邮件发送并不是我们主业务必须关注的结果,比如通知类、提醒类的业务可以允许延时或者失败。
- 这个时候可以采用异步的方式来发送邮件,加快主交易执行速度,在实际项目中可以采用 MQ 发送邮件相关参数,监听到消息队列之后启动发送邮件
总结
- 使用 Spring Boot 集成发送邮件的功能非常简单,只需要简单编码就可以实现发送普通文本邮件、带附件邮件、HTML 格式邮件、带图片邮件等
- 如果需要做成一个邮件系统还需要考虑很多因素,如邮箱发送失败重试机制、防止邮件被识别为垃圾邮件,固定时间内发送邮件的限制等
- 在微服务架构中,常常将一些基础功能下沉下来,作为独立的服务来使用,邮件系统作为一种和具体业务无关的功能,特别适合做为独立的微服务来支持整个系统