一. 首先你必须要有一个邮箱 126 163 等基本都可以,但是不排除有些邮箱不支持
步骤一: 开启邮箱的SMTP服务

开启126邮箱smtp服务.png
步骤二:开启授权码

image.png
二. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--本示例使用freemarker作为模板邮件的模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
三. 在application.properties添加邮箱配置
#协议
spring.mail.protocol=smtp
#发送者的邮箱账号,不要加@xx.com
spring.mail.username=ouranxxxx
#邮箱服务器,根据自己的邮箱设置
spring.mail.host=smtp.126.com
#发邮件send_name,必须跟邮箱保持一致,不然会报错
mail.send_username=ourxxxx6@126.com
#邮箱授权码,为了安全,使用授权码而不是密码
spring.mail.password=xxx123456
三. 发送邮件工具类
import freemarker.template.Configuration;
import freemarker.template.Template;
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.Service;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.validation.Valid;
import java.io.File;
import java.util.*;
@Service
public class JMailService {
private static final String DEFAULT_ENCODING = "utf-8";
@Value("${mail.send_username}")
private String SEND_USERNAME;
@Autowired
JavaMailSender javaMailSender;
private static final Logger logger = LoggerFactory.getLogger(JMailService.class);
private static Template template;
private static Configuration configuration;
static {
try {
//配置邮件模板路径
String templateDir = JMailService.class.getClassLoader().getResource("templates/email").getPath();
//创建配置对象
configuration = new Configuration(Configuration.getVersion());
//设置模板文件所在路径
configuration.setDirectoryForTemplateLoading(new File(templateDir));
//设置编码格式
configuration.setDefaultEncoding("utf-8");
} catch (Exception e) {
e.printStackTrace();
logger.error("freemarker template初始化失败");
}
}
/**
* 发送一个简单的文本邮件,可以附带附件:文本邮件发送的基本方法
*
* @param subject:邮件主题,即邮件的邮件名称
* @param content:邮件内容
* @param toWho:需要发送的人
* @param ccPeoples:需要抄送的人
* @param bccPeoples:需要密送的人
* @param attachments:需要附带的附件,附件请保证一定要存在,否则将会被忽略掉
*/
public void sendSimpleTextMail(String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples, String[] attachments) {
//检验参数:邮件主题、收件人、邮件内容必须不为空才能够保证基本的逻辑执行
if (subject == null || toWho == null || toWho.length == 0 || content == null) {
logger.error("邮件-> {} 无法继续执行,因为缺少基本的参数:邮件主题、收件人、邮件内容", subject);
throw new RuntimeException("模板邮件无法继续发送,因为缺少必要的参数!");
}
logger.info("开始发送简单文本邮件:主题->{},收件人->{},抄送人->{},密送人->{},附件->{}", subject, toWho, ccPeoples, bccPeoples, attachments);
//附件处理,需要处理附件时,需要使用二进制信息,使用 MimeMessage 类来进行处理
if (attachments != null && attachments.length > 0) {
try {
//附件处理需要进行二进制传输
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);
//设置邮件的基本信息:这些函数都会在后面列出来
boolean continueProcess = handleBasicInfo(helper, subject, content, toWho, ccPeoples, bccPeoples, false);
//如果处理基本信息出现错误
if (!continueProcess) {
logger.error("邮件基本信息出错: 主题->{}", subject);
return;
}
//处理附件
handleAttachment(helper, subject, attachments);
//发送该邮件
javaMailSender.send(mimeMessage);
logger.info("发送邮件成功: 主题->{}", subject);
} catch (Exception e) {
e.printStackTrace();
logger.error("发送邮件失败: 主题->{}", subject);
}
} else {
//创建一个简单邮件信息对象
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
//设置邮件的基本信息
handleBasicInfo(simpleMailMessage, subject, content, toWho, ccPeoples, bccPeoples);
//发送邮件
javaMailSender.send(simpleMailMessage);
logger.info("发送邮件成功: 主题->{}", subject, toWho, ccPeoples, bccPeoples, attachments);
}
}
/**
* 处理二进制邮件的基本信息,比如需要带附件的文本邮件、HTML文件、图片邮件、模板邮件等等
*
* @param mimeMessageHelper:二进制文件的包装类
* @param subject:邮件主题
* @param content:邮件内容
* @param toWho:收件人
* @param ccPeoples:抄送人
* @param bccPeoples:暗送人
* @param isHtml:是否是HTML文件,用于区分带附件的简单文本邮件和真正的HTML文件
* @return :返回这个过程中是否出现异常,当出现异常时会取消邮件的发送
*/
private boolean handleBasicInfo(MimeMessageHelper mimeMessageHelper, String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples, boolean isHtml) {
try {
//设置必要的邮件元素
//设置发件人
mimeMessageHelper.setFrom(SEND_USERNAME);
//设置邮件的主题
mimeMessageHelper.setSubject(subject);
//设置邮件的内容,区别是否是HTML邮件
mimeMessageHelper.setText(content, isHtml);
//设置邮件的收件人
mimeMessageHelper.setTo(toWho);
//设置非必要的邮件元素,在使用helper进行封装时,这些数据都不能够为空
if (ccPeoples != null && ccPeoples.length > 0)
//设置邮件的抄送人:MimeMessageHelper # Assert.notNull(cc, "Cc address array must not be null");
mimeMessageHelper.setCc(ccPeoples);
if (bccPeoples != null && ccPeoples.length > 0)
//设置邮件的密送人:MimeMessageHelper # Assert.notNull(bcc, "Bcc address array must not be null");
mimeMessageHelper.setBcc(bccPeoples);
return true;
} catch (MessagingException e) {
e.printStackTrace();
logger.error("邮件基本信息出错->{}", subject);
}
return false;
}
/**
* 用于填充简单文本邮件的基本信息
*
* @param simpleMailMessage:文本邮件信息对象
* @param subject:邮件主题
* @param content:邮件内容
* @param toWho:收件人
* @param ccPeoples:抄送人
* @param bccPeoples:暗送人
*/
private void handleBasicInfo(SimpleMailMessage simpleMailMessage, String subject, String content, String[] toWho, String[] ccPeoples, String[] bccPeoples) {
//设置发件人
simpleMailMessage.setFrom(SEND_USERNAME);
//设置邮件的主题
simpleMailMessage.setSubject(subject);
//设置邮件的内容
simpleMailMessage.setText(content);
//设置邮件的收件人
simpleMailMessage.setTo(toWho);
//设置邮件的抄送人
simpleMailMessage.setCc(ccPeoples);
//设置邮件的密送人
simpleMailMessage.setBcc(bccPeoples);
}
/**
* 用于处理附件信息,附件需要 MimeMessage 对象
*
* @param mimeMessageHelper:处理附件的信息对象
* @param subject:邮件的主题,用于日志记录
* @param attachmentFilePaths:附件文件的路径,该路径要求可以定位到本机的一个资源
*/
private void handleAttachment(MimeMessageHelper mimeMessageHelper, String subject, String[] attachmentFilePaths) {
//判断是否需要处理邮件的附件
if (attachmentFilePaths != null && attachmentFilePaths.length > 0) {
FileSystemResource resource;
String fileName;
//循环处理邮件的附件
for (String attachmentFilePath : attachmentFilePaths) {
//获取该路径所对应的文件资源对象
resource = new FileSystemResource(new File(attachmentFilePath));
//判断该资源是否存在,当不存在时仅仅会打印一条警告日志,不会中断处理程序。
// 也就是说在附件出现异常的情况下,邮件是可以正常发送的,所以请确定你发送的邮件附件在本机存在
if (!resource.exists()) {
logger.warn("邮件->{} 的附件->{} 不存在!", subject, attachmentFilePath);
//开启下一个资源的处理
continue;
}
//获取资源的名称
fileName = resource.getFilename();
try {
//添加附件
mimeMessageHelper.addAttachment(fileName, resource);
} catch (MessagingException e) {
e.printStackTrace();
logger.error("邮件->{} 添加附件->{} 出现异常->{}", subject, attachmentFilePath, e.getMessage());
}
}
}
}
/**
* 可以用来发送带有图片的HTML模板邮件
*
* @param subject:邮件主题
* @param toWho:收件人
* @param ccPeoples:抄送人
* @param bccPeoples:暗送人
* @param attachments:附件
* @param templateName:模板名称
* @param context:模板解析需要的数据
* @param imageResourceSet:图片资源的资源对象
*/
public void sendHtmlTemplateMail(String subject, String[] toWho, String[] ccPeoples, String[] bccPeoples, String[] attachments, String templateName, Map context, Set<ImageResource> imageResourceSet) {
//加载指定的模板
try {
template = configuration.getTemplate(templateName);
//检验参数:邮件主题、收件人、模板名称必须不为空才能够保证基本的逻辑执行
if (subject == null || toWho == null || toWho.length == 0 || templateName == null) {
logger.error("邮件-> {} 无法继续执行,因为缺少基本的参数:邮件主题、收件人、模板名称", subject);
throw new RuntimeException("模板邮件无法继续发送,因为缺少必要的参数!");
}
//日志这个邮件的基本信息
logger.info("发送HTML模板邮件:主题->{},收件人->{},抄送人->{},密送人->{},附件->{},模板名称->{},模板解析参数->{},图片资源->{})", subject, toWho, ccPeoples, bccPeoples, attachments, templateName, context, imageResourceSet);
//context不能够为空,需要进行检查
if (context == null) {
context = new HashMap();
logger.info("邮件->{} 的context为空!", subject);
}
//模板引擎处理模板获取到HTML字符串
String content = FreeMarkerTemplateUtils.processTemplateIntoString(template, context);
;
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
//默认编码为UTF-8
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);
//处理内联的图片资源的占位转换
content = handleInLineImageResourceContent(helper, subject, content, imageResourceSet);
logger.info("解析邮件结果->{}", content);
//处理基本信息
boolean continueProcess = handleBasicInfo(helper, subject, content, toWho, ccPeoples, bccPeoples, true);
if (!continueProcess) {
logger.error("邮件基本信息出错:主题->{}", subject);
return;
}
//内联资源的资源附加,这个必须要放置在设置基本信息的操作后面,或者是全部内容解析完毕后才可以,不能边解析,边占位
handleInLineImageResource(helper, subject, imageResourceSet);
//处理附件
handleAttachment(helper, subject, attachments);
//发送该邮件
javaMailSender.send(mimeMessage);
logger.info("发送邮件成功:主题->{}", subject);
} catch (Exception e) {
e.printStackTrace();
logger.error("发送邮件失败:" + e.getMessage() + "邮件主题->{}", subject);
}
}
/**
* 处理内嵌图片的模板HTML邮件,返回一个已经修改过后的HTML字符串
*
* @param mimeMessageHelper:邮件信息包装类
* @param subject:邮件主题
* @param originContent:模板引擎所解析出来的原始HTML邮件
* @param imageResourceSet:图片资源集合,用于字符集站位填充
* @return :返回处理后的邮件内容
*/
private String handleInLineImageResourceContent(MimeMessageHelper mimeMessageHelper, String subject, String originContent, Set<ImageResource> imageResourceSet) {
//处理内嵌的HTML图片文件
if (imageResourceSet != null && imageResourceSet.size() > 0) {
//资源的占位符ID
String rscId;
//资源的路径
String resourcePath = null;
//图片的位置信息
String placeHolder;
//图片资源文件
FileSystemResource resource;
for (ImageResource imageResource : imageResourceSet) {
//获取图片资源的基本信息
rscId = imageResource.getId();
placeHolder = imageResource.getPlaceholder();
resourcePath = imageResource.getImageFilePath();
resource = new FileSystemResource(new File(resourcePath));
//判断图片资源是否存在
if (!resource.exists()) {
logger.warn("邮件->{} 内联图片->{} 找不到", subject, resourcePath);
continue;
}
//替换图片资源在HTML中的位置
originContent = originContent.replace("\"" + ImageResource.PLACEHOLDERPREFIX + placeHolder + "\"", "\'cid:" + rscId + "\'");
}
}
return originContent;
}
/**
* 填充文本数据,因为数据填充必须在设置基本数据后面进行,所以讲内容和数据的填充进行分离
*
* @param mimeMessageHelper
* @param subject:邮件主题,用于日志记录
* @param imageResourceSet:资源
*/
private void handleInLineImageResource(MimeMessageHelper mimeMessageHelper, String subject, Set<ImageResource> imageResourceSet) {
if (imageResourceSet != null && imageResourceSet.size() > 0) {
FileSystemResource resource;
for (ImageResource imageResource : imageResourceSet) {
resource = new FileSystemResource(new File(imageResource.getImageFilePath()));
if (!resource.exists()) {
logger.warn("邮件->{} 的内联图片文件->{} 不存在!", subject, imageResource);
continue;
}
try {
//添加内联资源
mimeMessageHelper.addInline(imageResource.getId(), resource);
} catch (MessagingException e) {
e.printStackTrace();
logger.error("邮件->{} 的内联图片文件->{} 添加错误!", subject, imageResource);
}
}
}
}
/**
* 传入的参数不能够为null
*
* @param args
* @return
*/
private boolean assertNotNull(Object... args) {
return Arrays.stream(args).noneMatch(Objects::isNull);
}
/**
* 用于支撑HTML内嵌图片的支持类,拥有可以传输内联图片的全部基本信息
*/
public final static class ImageResource {
//占位符的前缀符号,用于替换字符串定位,比如:image1 在模板文件里面需要写成 #image1
public static final String PLACEHOLDERPREFIX = "#";
//用于文件区分,实现图片文件内联邮件发送
private final String id;
//这个图片需要填充到那个地方去,这个地方是一个标识,为了和其他标签区别开来,使用前缀加上标识符来进行区分,比如 :#imageOrigin
private final String placeholder;
//图片的文件路径,该文件路径必须是本机文件系统的绝对路径,即可以直接 new File 的文件系统路径
private final String imageFilePath;
public ImageResource(String placeholder, String imageFilePath) {
this.placeholder = placeholder;
this.imageFilePath = imageFilePath;
//自动生成id,用于区分图片文件
this.id = String.valueOf(System.nanoTime());
}
public String getId() {
return id;
}
public String getPlaceholder() {
return placeholder;
}
public String getImageFilePath() {
return imageFilePath;
}
@Override
public String toString() {
return "ImageResource{" + "id=" + id + ", placeholder='" + placeholder + '\'' + ", imageFilePath='" + imageFilePath + '\'' + '}';
}
}
}
测试类
package cn.wolfcode.shop.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.mail.MessagingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Controller
public class MailTestController {
@Autowired
JMailService jMailService;
@Autowired
private JavaMailSender mailSender;
//请把如下的信息写成你自己的邮箱信息
//密送邮箱
private String mail2 = "10xxxx2926@qq.com";
//抄送邮箱
private String mail3 = "474xxxx78@qq.com";
private String mail4 = "ourxxxx6@aliyun.com";
//收件人邮箱
private String mail5 = "ouraxxxx6@126.com";
//邮件内容
private String content = "简单的文本内容";
//三个附件
private String zipFile = "C:\\Users\\Administrator\\Desktop\\abc.zip";
private String pngFile = "C:\\Users\\Administrator\\Desktop\\abc.png";
private String docFile = "C:\\Users\\Administrator\\Desktop\\abc.docx";
@Autowired
JavaMailSender javaMailSender;
//最简单测试
@GetMapping("/mail")
@ResponseBody
public String contextLoads() {
//建立邮件消息
SimpleMailMessage mainMessage = new SimpleMailMessage();
//发送者
mainMessage.setFrom(mail5);
//接收者
mainMessage.setTo(mail5);
//发送的标题
mainMessage.setSubject("测试简单邮件发送");
//发送的内容
mainMessage.setText("System.out.println(hello world)");
javaMailSender.send(mainMessage);
return "success";
}
/**
* 测试发送文本邮件的接口
*/
@GetMapping("/textmail")
@ResponseBody
public String sendSimpleTextMail() throws InterruptedException{
//发送一封邮件,抄送给两个邮箱,带着三个附件
jMailService.sendSimpleTextMail("测试带附件、有抄送、密送的多收件人简单文本文件",content,new String[]{mail5},
null,null,new String[]{zipFile, docFile,pngFile});
return "success";
}
/***
* 测试模板HTML邮件
*/
@GetMapping("/templatemail")
@ResponseBody
public String sendHtmlTemplateMail() throws MessagingException {
//模板解析的上下文
Map context = new HashMap<>();
context.put("name","秦羽");
context.put("age","20");
context.put("sex","男");
context.put("token","123456");
//邮箱中显示图片需要经过处理:设置内联的图片资源,placeholder和html模板中占位符对应
JMailService.ImageResource imageResource1 = new JMailService.ImageResource("phA",pngFile);
Set<JMailService.ImageResource> imageResources = new HashSet<>();
imageResources.add(imageResource1);
jMailService.sendHtmlTemplateMail(
"测试模板邮件", //主题
new String[]{mail5}, //收件人
null, //抄送人
null, //密送人
new String[]{zipFile,docFile}, //附件
"validate.ftl", //模板名称
context, //数据模型
imageResources); //需要处理的图片资源
return "success";
}
/**
* 测试使用邮箱完成注册用户校验
* @return
*/
@GetMapping("/validate")
@ResponseBody
public String testValidate(String token){
System.out.println("=============测试使用邮件校验,token "+token);
return "you mail validate accept ...";
}
}
需要用到的资源
- 三个附件文件
2.validate.ftl 放在springboot项目的 templates/emial文件夹下
validate.ftl文件
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>邮件模板</title>
</head>
<body>
<h2>你好!这是一份来自未来的邮件!</h2>
<p>姓名 <span>${name}</span></p><br/>
<p>年龄 <span>${age}</span></p><br/>
<p>性别 <span>${sex}</span></p><br/>
<img src="#phA" width="100px" height="100px"/><br/>
<a href="localhost:80/validate?token=${token}" target="_blank" rel="noreferrer">请点击链接校验您的邮箱</a>
</body>
</html>
============================完=========================