目录
案例:员工入职-发送短信和邮件告知相关信息;企业发放Offer;
用户注册发送用于激活账号的链接;OA系统不同人员协同工作途径....
核心步骤:申请用于发送邮件的"主题邮箱"(一般企业都会 有一个主体邮箱) 在qq邮箱的账户里开启SMTP服务,然后再生成授权码即可。
加入起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>${mail.version}</version>
</dependency>
在 application.properties中配置
#邮件配置
spring.mail.host=smtp.qq.com
spring.mail.username=xxx
spring.mail.password=xxx
#hbvrvyvkpiqpjdaa
#保证一些内容安全性的加密
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
mail.send.from=xxxx
先写前端的请求类:MailRequest.java
@Data
public class MailRequest implements Serializable {
@NotBlank(message = "邮箱主题不能为空")
private String subject;
@NotBlank(message = "邮箱内容不能为空")
private String content;
@NotBlank(message = "接收人不能为空")
private String tos;
}
发送简单文本邮件
Service层
@Service
public class MailService {
private static final Logger log = LoggerFactory.getLogger(MailService.class);
@Autowired
private JavaMailSender mailSender;
@Autowired
private Environment env;
//发送简单文本文件
public void sendSimpleTextMail(MailDto mailDto){
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(env.getProperty("mail.send.from"));
message.setTo(mailDto.getTos());
message.setSubject(mailDto.getSubject());
message.setText(mailDto.getContent());
mailSender.send(message);
log.info("----发送简单文本邮件成功----");
}
}
Controller层
//发送邮件controller
@RestController
@RequestMapping("mail")
public class MailController extends AbstractController{
@Autowired
private MailService mailService;
//发送简单文本文件
@RequestMapping(value = "simple", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse simpleTextMail(@RequestBody @Validated MailRequest mailRequest, BindingResult result){
String checkRes = ValidatorUtil.checkResult(result);
if(StringUtils.isNotBlank(checkRes)){
return new BaseResponse(StatusCode.InvalidParam.getCode(), checkRes);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try{
log.info("--发送邮件controller-发送简单文本文件:{}");
MailDto mailDto = new MailDto();
//复制的时候省略掉tos
BeanUtils.copyProperties(mailRequest, mailDto, "tos");
mailDto.setTos(StringUtils.split(mailRequest.getTos(), ","));
mailService.sendSimpleTextMail(mailDto);
}catch (Exception e){
response = new BaseResponse(StatusCode.Fail);
}
return response;
}
}
结果
发送HTML文件的邮件
内容:其实就是邮件的内容带上了HTML文本的标签,让邮件看起来更好看!
Service层
//发送HTML文本文件
public void sendHtmlMail(MailDto mailDto) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
//利用MimeMessageHelper对message进行加工
//multipart:表示是否是富文本
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8");
messageHelper.setFrom(env.getProperty("mail.send.from"));
messageHelper.setTo(mailDto.getTos());
messageHelper.setSubject(mailDto.getSubject());
//html:设置为true
messageHelper.setText(mailDto.getContent(), true);
mailSender.send(message);
log.info("----发送HTML文本邮件成功----");
}
Controller层
//发送HTML文本文件
@RequestMapping(value = "html", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse htmlMail(@RequestBody @Validated MailRequest mailRequest, BindingResult result){
String checkRes = ValidatorUtil.checkResult(result);
if(StringUtils.isNotBlank(checkRes)){
return new BaseResponse(StatusCode.InvalidParam.getCode(), checkRes);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try{
log.info("--发送邮件controller-发送HTML文本文件:{}", mailRequest);
MailDto mailDto = new MailDto();
//复制的时候省略掉tos
BeanUtils.copyProperties(mailRequest, mailDto, "tos");
mailDto.setTos(StringUtils.split(mailRequest.getTos(), ","));
mailService.sendHtmlMail(mailDto);
}catch (Exception e){
response = new BaseResponse(StatusCode.Fail);
}
return response;
}
运行结果
发送带附件的邮件
内容:其实就是发送邮件的时候附带上文件而已!
application.properties-设置附件路径以及附件的名字
mail.send.attachment.one.name=图片1.jpg
mail.send.attachment.one.location=${mail.send.attachment.root.url}\\1.jpg
mail.send.attachment.two.name=图片2.png
mail.send.attachment.two.location=${mail.send.attachment.root.url}\\2.png
service-基本不变,额外调用了addAttachment()接口,如果文件名超过60的话会发生乱码问题,解决方案:直接初始化
//发送带附件的邮件
public void sendAppendixMail(MailDto mailDto) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
//利用MimeMessageHelper对message进行加工
//multipart:表示是否是富文本
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8");
messageHelper.setFrom(env.getProperty("mail.send.from"));
messageHelper.setTo(mailDto.getTos());
messageHelper.setSubject(mailDto.getSubject());
//html:设置为true
messageHelper.setText(mailDto.getContent(), true);
//TODO:在messageHelper里面加入附件
messageHelper.addAttachment(env.getProperty("mail.send.attachment.one.name"), new File(env.getProperty("mail.send.attachment.one.location")));
messageHelper.addAttachment(env.getProperty("mail.send.attachment.two.name"), new File(env.getProperty("mail.send.attachment.two.location")));
//TODO:当附件的文件名长度大于60 || splitlongparameters=true的时候,会出现附件乱码等其他奇奇怪怪的现象
//解决方案:在bean初始化后后添加一个init方法,设置对应的参数为falise
mailSender.send(message);
log.info("----发送带附件的邮件成功----");
}
//在系统启动后初始化相应的参数
@PostConstruct
public void init(){
System.setProperty("mail.mime.splitlongparameters", "false");
}
controller-没有任何变化
//发送带附件的邮件
@RequestMapping(value = "appendix", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse appendixMail(@RequestBody @Validated MailRequest mailRequest, BindingResult result){
String checkRes = ValidatorUtil.checkResult(result);
if(StringUtils.isNotBlank(checkRes)){
return new BaseResponse(StatusCode.InvalidParam.getCode(), checkRes);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try{
log.info("--发送邮件controller-发送带有附件的邮件:{}", mailRequest);
MailDto mailDto = new MailDto();
//复制的时候省略掉tos
BeanUtils.copyProperties(mailRequest, mailDto, "tos");
mailDto.setTos(StringUtils.split(mailRequest.getTos(), ","));
mailService.sendAppendixMail(mailDto);
}catch (Exception e){
response = new BaseResponse(StatusCode.Fail);
}
return response;
}
基于Freemarker发送带HTML模板的邮件
内容:不管发送多少次,邮件的内容大部分的话术是一样的,针对这些共同的术语,我们可以将其抽取出来放到"HTML模板"中。而对于那些变化的部分,我们只需要往模板塞入"对应的取值"即可,比如接受人的邮箱等等!
freemarker模板 目录/ftl/mail.ftl
<p>你好, ${mailTos} 欢迎您!</p>
<p><strong style="...">希望在未来的日子里,能够和大家一块成长</strong> </p>
service
//渲染模板
public String renderTemplate(final String templateFile, Map<String, Object> paramMap) throws Exception{
Configuration cfg = new Configuration(Configuration.VERSION_2_3_26);
//TODO:设置ftl模板所在的根目录
cfg.setClassForTemplateLoading(this.getClass(), "/ftl");
Template template = cfg.getTemplate(templateFile);
return FreeMarkerTemplateUtils.processTemplateIntoString(template, paramMap);
}
//发送带附件的邮件
public void sendHTMLTemplateMail(MailDto mailDto) throws Exception {
MimeMessage message = mailSender.createMimeMessage();
//利用MimeMessageHelper对message进行加工
//multipart:表示是否是富文本
MimeMessageHelper messageHelper = new MimeMessageHelper(message, true, "utf-8");
messageHelper.setFrom(env.getProperty("mail.send.from"));
messageHelper.setTo(mailDto.getTos());
messageHelper.setSubject(mailDto.getSubject());
//html:设置为true
//渲染模板,得到html邮件内容
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("mailTos", Arrays.toString(mailDto.getTos()));
String content = this.renderTemplate(env.getProperty("mail.template.file.location"), dataMap);
messageHelper.setText(content, true);
mailSender.send(message);
log.info("----发送带附件的邮件成功----");
}
controller
@RequestMapping(value = "html/template",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse htmlTemplateMail(@RequestBody @Validated MailRequest mailRequest, BindingResult result){
String checkRes = ValidatorUtil.checkResult(result);
if(StringUtils.isNotBlank(checkRes)){
return new BaseResponse(StatusCode.InvalidParam.getCode(), checkRes);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try{
log.info("--发送邮件controller-发送HTML模板的邮件:{}", mailRequest);
MailDto mailDto = new MailDto();
//复制的时候省略掉tos
BeanUtils.copyProperties(mailRequest, mailDto, "tos");
mailDto.setTos(StringUtils.split(mailRequest.getTos(), ","));
mailService.sendHTMLTemplateMail(mailDto);
}catch (Exception e){
response = new BaseResponse(StatusCode.Fail);
}
return response;
}
多线程Executors群发邮件
基于Executors-多线程群发邮件,减少单线程群发邮件的时间与压力!
案例:给所有有效用户推送一条"春节祝福语"或者"App重大版本更新消息"
新建一张user表
create table user(
id int(11) not null auto_increment,
name varchar(255) character set utf8mb4 default null comment "名字",
code varchar(255) character set utf8mb4 default null comment "工号",
email varchar(255) character set utf8mb4 default null comment "邮箱",
is_active int(11) default 1,
primary key(id)
)engine=InnoDB auto_increment=343587 default charset=utf8 comment="用户信息表";
select * from user;
insert into user(name, code, email) values ("aaa", "1234", "1577153977@qq.com");
insert into user(name, code, email) values ("bbb", "5678", "1784287593@163.com");
insert into user(name, code, email) values ("ccc", "5679", "1784287593@126.com");
insert into user(name, code, email) values ("ddd", "5", "1@qq.com");
insert into user(name, code, email) values ("eee", "6", "2@qq.com");
insert into user(name, code, email) values ("fff", "7", "3@qq.com");
insert into user(name, code, email) values ("ggg", "8", "4@qq.com");
insert into user(name, code, email) values ("hhh", "9", "5@qq.com");
通过逆向工程生成Userm UserMapper, UserMapper.xml
创建发送邮件的ThreadMailDto
//单个线程要做的任务-给邮箱发送邮件
public class ThreadMailDto implements Callable<Boolean> {
private MailDto mailDto;
private MailService mailService;
public ThreadMailDto(MailDto mailDto, MailService mailService) {
this.mailDto = mailDto;
this.mailService = mailService;
}
//此处为每个线程实例执行的任务
public Boolean call() throws Exception {
mailService.sendHTMLTemplateMail(mailDto);
return true;
}
}
service层:创建线程池去实现
@Service
public class ThreadMailService {
private static final Logger log = LoggerFactory.getLogger(ThreadMailService.class);
@Autowired
private UserMapper userMapper;
@Autowired
private MailService mailService;
//多线程群发邮件
public void sendThreadsMail(final MailDto dto) throws Exception{
Set<String> set = userMapper.selectAllMails();
if(set != null && !set.isEmpty(s)) {
//TODO:并发多线程实例
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<ThreadMailDto> list = Lists.newLinkedList();
//遍历set(邮箱集合)中的每个元素,构造成一个ThreadMailDto对象
set.forEach(email -> {
dto.setTos(new String[]{email});
list.add(new ThreadMailDto(dto, mailService));
});
executorService.invokeAll(list);
}
log.info("......多线程群发邮件成功.......");
}
}
Controller层
//多线程群发邮件
@RequestMapping(value = "threads",method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse threadsMail(@RequestBody @Validated MailRequest mailRequest, BindingResult result){
String checkRes = ValidatorUtil.checkResult(result);
if(StringUtils.isNotBlank(checkRes)){
return new BaseResponse(StatusCode.InvalidParam.getCode(), checkRes);
}
BaseResponse response = new BaseResponse(StatusCode.Success);
try{
log.info("--发送邮件controller-多线程群发邮件:{}", mailRequest);
MailDto mailDto = new MailDto();
//复制的时候省略掉tos
BeanUtils.copyProperties(mailRequest, mailDto, "tos");
mailDto.setTos(StringUtils.split(mailRequest.getTos(), ","));
threadMailService.sendThreadsMail(mailDto);
}catch (Exception e){
response = new BaseResponse(StatusCode.Fail);
}
return response;
}