发送邮件常用的有163邮箱,qq邮箱,其中发送邮件的协议叫SMTP,接收邮件的协议叫POP3/IMAP,IMAP协议比POP3更强大,不过我们不需要要关注,因为服务器集成邮件只会涉及到发送邮件,一般不涉及接收邮件。
我们已163邮箱为例来讲解,首先要开通允许客户端发送邮件的功能
登录进163邮箱后,点击设置
选择开启服务,下面两个随便哪个都可以,点击开启后会提示扫码发送短信,发完短信会显示授权码, 这个授权码只会显示一次,要记录下来,后面会用到
接下来就是代码阶段了
org.springframework.boot
spring-boot-starter-mail
spring:
mail:
#smtp服务主机 qq邮箱则为smtp.qq.com
host: smtp.163.com
port: 465 #端口不要使用默认的25,阿里云无法开通这个端口,使用465
protocol: smtps
# 编码集
default-encoding: UTF-8
#发送邮件的账户
username: [email protected]
#授权码
password: xxxxxx
test-connection: true
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
@Service
public class EmailService {
@Value("${spring.mail.username}")
private String from;
@Autowired
private JavaMailSender mailSender;
/**
* 发送纯文本邮件
*
* @param tos 接收方
* @param subject 主题
* @param content 邮件内容
* @return
*/
public void sendTxtMail(String[] tos, String subject, String content) {
//创建简单邮件消息
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(from);
message.setTo(tos);
message.setSubject(subject);
message.setText(content);
try {
mailSender.send(message);
} catch (MailException e) {
e.printStackTrace();
throw new ServiceFailException("发送邮件失败", e);
}
}
}
public void sendHtmlEmail(String[] tos, String subject, String html) {
try {
//创建一个MINE消息
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper minehelper = new MimeMessageHelper(message, true);
minehelper.setFrom(from);
minehelper.setTo(tos);
minehelper.setSubject(subject);
//邮件内容 true 表示带有附件或html
minehelper.setText(html, true);
mailSender.send(message);
} catch (Exception e) {
throw new ServiceFailException("发送邮件失败", e);
}
}
由于发送附件的时间较长,所有我们用@Async注解做成异步的,SpringBoot的异步线程只有一个,所以并发量大的话会有延迟,有条件的话可以把发送邮件的任务放入MQ,然后从MQ中取出再执行。需要注意的是发送Multipart不能异步,因为Multipart文件在上传时会创建一个临时文件,一旦请求结束这个文件就会被删除
/**
* 发送带附件的邮件,附件格式为Multipart
*
* @param tos
* @param subject
* @param html
* @param files
*/
public void sendMultipartEmail(String[] tos, String subject, String html, List files) {
List list = new ArrayList<>();
List resourceBeans = new ArrayList<>();
if (CollectionUtils.isNotEmpty(files)) {
for (MultipartFile multipartFile : files) {
//把multipart转成file
Optional optionalFile = FileUtils.multipartFileToFile(multipartFile);
//存在则放入list
optionalFile.ifPresent(file -> {
list.add(file);
resourceBeans.add(new ResourceBean(new FileSystemResource(file), multipartFile.getOriginalFilename()));
});
}
}
sendResourceEmail(tos, subject, html, resourceBeans);
//发送完删除临时文件
for (File file : list) {
file.delete();
}
}
/**
* 发送带附件的邮件,File格式
*
* @param tos
* @param subject
* @param html
* @param files
*/
@Async
public void sendHtmlEmail(String[] tos, String subject, String html, List files) {
List resourceBeans = files.stream().map(file -> new ResourceBean(new FileSystemResource(file), file.getName())).collect(Collectors.toList());
sendResourceEmail(tos, subject, html, resourceBeans);
}
private void sendResourceEmail(String[] tos, String subject, String html, List resourceBeans) {
//创建一个MINE消息
MimeMessage message = mailSender.createMimeMessage();
try {
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(from);
helper.setTo(tos);
helper.setSubject(subject);
//邮件内容 true 表示带有附件或html
helper.setText(html, true);
if (CollectionUtils.isNotEmpty(resourceBeans)) {
for (ResourceBean resourceBean : resourceBeans) {
//添加附件
helper.addAttachment(resourceBean.getFilename(), resourceBean.getResource());
}
}
mailSender.send(message);
} catch (Exception e) {
throw new ServiceFailException("发送邮件失败", e);
}
}
@Getter
@Setter
@AllArgsConstructor
static class ResourceBean {
FileSystemResource resource;
String filename;
}
下面是MultipartFile转File的方法
/**
* multiFile转成file,在本地生成一个文件,文件名随机,使用完需删除
*
* @param multiFile 要转换的文件
* @return
*/
public static Optional multipartFileToFile(MultipartFile multiFile) {
// 获取文件名
String fileName = multiFile.getOriginalFilename();
// 获取文件后缀
String prefix = getExtention(fileName);
try {
File file = File.createTempFile(UUIDUtils.getUUID(), prefix);
file.deleteOnExit();
multiFile.transferTo(file);
return Optional.of(file);
} catch (Exception e) {
e.printStackTrace();
}
return Optional.empty();
}
我们使用thymeleaf作为模板来发送邮件,首先引入依赖
org.springframework.boot
spring-boot-starter-thymeleaf
加入配置项,所有模板放在类路径的templates下面并且以html结尾
spring:
thymeleaf:
cache: false
mode: LEGACYHTML5
prefix: classpath:/templates/
suffix: .html
然后在建一个mail.html
邮件模板
您好,感谢您的注册,这时一封验证邮件,请点击下面的链接完成注册!
点我完成注册
使用templateEngine拿到模板和参数,渲染成html, 之后就跟发送html邮件一样了
@Async
public void sendTemplateEmail(String[] tos, String subject, String templateName, Map params) {
Context context = new Context();
//设置参数
if (params != null) {
Set> entrySet = params.entrySet();
entrySet.stream().forEach(entry -> context.setVariable(entry.getKey(), entry.getValue()));
}
//渲染模板,获得html内容
String html = templateEngine.process("email/" + templateName, context);
sendHtmlEmail(tos, subject, html);
}