文章目录
- 简介
- 依赖导入
- 配置
- 核心类介绍
- 申请邮箱授权码
- 发送邮件示例
- 发送简单文本邮件
- 发送Html格式邮件
- 发送带附件的邮件
- 发送带静态资源的邮件
- 发送模板邮件
- 扩展
- 自定义发件人名称
- 发送多人、抄送、密送
- 常见的错误返回码
- 发送频率和次数限制
- 企业邮箱发送邮件
- 连接smtp.qq.com 25异常问题
简介
信息通知类功能,目前的实现有短信、邮件、钉钉等较为普遍,其中邮件功能成本最为低廉,消息存储溯源性高,常用于注册验证,忘记密码或者是给用户发送信息。
依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
配置
对应类为:MailProperties.java
spring:
#邮箱配置
mail:
host: smtp.qq.com
username: xxxxxx@qq.com
#QQ邮箱的授权码
password:
#默认UTF-8
#default-encoding: UTF-8
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
也可以使用Java配置
@Bean
public JavaMailSender getJavaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("my.gmail@gmail.com");
mailSender.setPassword("password");
Properties props = mailSender.getJavaMailProperties();
props.put("mail.transport.protocol", "smtp");
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.debug", "true");
return mailSender;
}
配置文件的配置会覆盖Java配置
核心类介绍
- MailSender:提供发送简单电子邮件的基本功能的顶级接口
- JavaMailSender接口:上述MailSender的子接口。它支持 MIME 消息,并且主要与MimeMessageHelper类结合使用以创建MimeMessage。建议在此接口中使用MimeMessagePreparator机制。
- JavaMailSenderImpl类: 提供了JavaMailSender接口的实现。它支持MimeMessage和SimpleMailMessage。
- SimpleMailMessage类:用于创建简单的邮件消息,包括发件人、收件人、抄送、主题和文本字段
- MimeMessagePreparator接口:提供了一个回调接口,用于准备 MIME 消息。
- MimeMessageHelper类:用于创建 MIME 消息的帮助器类。它支持 HTML 布局中的图像、典型邮件附件和文本内容。
申请邮箱授权码
以QQ邮箱为例,申请流程如下:
QQ邮箱->设置->账户->POP3/SMTP服务:开启服务后会获得QQ的授权码
发送邮件示例
发送简单文本邮件
@Autowired
JavaMailSender javaMailSender;
public void sendSimpleMail() {
try {
SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
simpleMailMessage .setFrom("发送方邮箱");
simpleMailMessage .setTo("接收方");
simpleMailMessage.setSubject("主题");
simpleMailMessage.setText("内容");
javaMailSender.send(simpleMailMessage);//发送
} catch (Exception e) {
logger.error("邮件发送失败", e.getMessage());
}
}
发送Html格式邮件
public void sendHtmlMail() {
try {
MimeMessage mimeMailMessage = javaMailSender.createMimeMessage();
//true表示需要创建一个multipart message
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true);
mimeMessageHelper.setFrom("发送者邮箱");
mimeMessageHelper.setTo("接收者邮箱");
mimeMessageHelper.setSubject("主题");
mimeMessageHelper.setText("<h1>hello</h1>", true);
javaMailSender.send(mimeMailMessage);
logger.info("html邮件发送成功");
} catch (MessagingException e) {
logger.error("发送html邮件时发生异常!", e);
}
}
发送带附件的邮件
public void sendAttachmentMail() {
try {
MimeMessage mimeMailMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true);
mimeMessageHelper.setFrom("发送者邮箱");
mimeMessageHelper.setTo("接收者邮箱");
mimeMessageHelper.setSubject("主题");
mimeMessageHelper.setText("<h1>hello</h1>", true);
//文件路径 spring的FileSystemResource,使用绝对路径访问文件资源
FileSystemResource file = new FileSystemResource(new File("src/main/resources/static/image/i.jpg"));
mimeMessageHelper.addAttachment("i.jpg", file);
javaMailSender.send(mimeMailMessage);
} catch (Exception e) {
logger.error("邮件发送失败", e.getMessage());
}
}
发送带静态资源的邮件
public void sendInlineMail() {
try {
MimeMessage mimeMailMessage = javaMailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true);
mimeMessageHelper.setFrom("发送者邮箱");
mimeMessageHelper.setTo("接收者邮箱");
mimeMessageHelper.setSubject("主题");
mimeMessageHelper.setText("<html><body>带静态资源的邮件内容,这个一张照片:<img src='cid:picture' /></body></html>", true);
//文件路径
FileSystemResource file = new FileSystemResource(new File("src/main/resources/static/image/i.jpg"));
mimeMessageHelper.addInline("picture", file);
javaMailSender.send(mimeMailMessage);
} catch (Exception e) {
logger.error("邮件发送失败", e.getMessage());
}
}
发送模板邮件
第一步:引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
第二步:创建模板,在resources/templates
下新建xxx.ftl
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<div>
<h2>邮件消息通知</h2>
<table border="1">
<tr>
<th>工号</th>
<th>工龄</th>
<th>薪资</th>
</tr>
<tr>
<td>${(jobNo)!""}</td>
<td>${(jobAge)!""}</td>
<td>${(salary)!""}</td>
</tr>
</table>
</div>
</body>
</html>
第三步:解析模板发送邮件
@Autowired
JavaMailSender javaMailSender;
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Value("${spring.mail.username}")
String from;
@Test
public void sendTemplateMail() {
try {
MimeMessage mimeMailMessage = javaMailSender.createMimeMessage();
String nick = "";
try {
nick = javax.mail.internet.MimeUtility.encodeText("laker测试");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 设置发件人
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMailMessage, true);
mimeMessageHelper.setFrom(String.valueOf(new InternetAddress(from, nick)));
mimeMessageHelper.setTo("xxxx@qq.com");
mimeMessageHelper.setSubject("主题");
Map<String, Object> model = new HashMap<>();
model.put("jobNo", "30982");
model.put("jobAge", "30");
model.put("salary", "43800");
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("xxx.ftl");
String text = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
mimeMessageHelper.setText(text, true);
javaMailSender.send(mimeMailMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
扩展
自定义发件人名称
String nick = "";
try {
nick = javax.mail.internet.MimeUtility.encodeText("张三");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
// 设置发件人
simpleMailMessage.setFrom(String.valueOf(new InternetAddress("xxxxx@qq.com", nick)));
发送多人、抄送、密送
// 构建一个邮件对象
SimpleMailMessage message = new SimpleMailMessage();
// 设置邮件主题
message.setSubject("这是一封测试邮件");
// 设置邮件发送者,这个跟application.yml中设置的要一致
message.setFrom("7*****9@qq.com");
// 设置邮件接收者,可以有多个接收者,中间用逗号隔开,以下类似
// message.setTo("10*****16@qq.com","12****32*qq.com");
message.setTo("10*****16@qq.com");
// 设置邮件抄送人,可以有多个抄送人
message.setCc("12****32*qq.com");
// 设置隐秘抄送人,可以有多个
message.setBcc("7******9@qq.com");
// 设置邮件发送日期
message.setSentDate(new Date());
常见的错误返回码
- 421 HL:ICC 该IP同时并发连接数过大,超过了网易的限制,被临时禁止连接。
- 451 Requested mail action not taken: too much fail authentication 登录失败次数过多,被临时禁止登录。请检查密码与帐号验证设置
- 553 authentication is required,密码配置不正确
- 554 DT:SPM 发送的邮件内容包含了未被许可的信息,或被系统识别为垃圾邮件。请检查是否有用户发送病毒或者垃圾邮件;
- 550 Invalid User 请求的用户不存在
- 554 MI:STC 发件人当天内累计邮件数量超过限制,当天不再接受该发件人的投信。如果使用一个邮箱频繁发送相同内容邮件,也会被认定为垃圾邮件,报 554 DT:SPM 错误
- 501 Mail from address must be same as authorization user.发送者必须要和SMTP中的账号一致
message.setFrom(new InternetAddress(“xxx@qq.com”));
要与connect保持一致,connect中是xxx@qq.com,那么from中也必须是xxx@qq.com
connect(sender, password);// 密码为QQ邮箱开通的SMTP服务后得到的客户端授权码
发送频率和次数限制
注意发送过快过多会报535错误,建议每10秒发送一次或者没分钟一次
也可以改用企业邮箱,会好很多
企业邮箱发送邮件
mail:
# 这个地方不一样
host: smtp.exmail.qq.com
username: xxxxx@qq.com
# 企业邮箱这里是邮箱的登录密码,没有授权码了
password: xxxx
连接smtp.qq.com 25异常问题
Caused by: com.sun.mail.util.MailConnectException: Couldn't connect to host, port: smtp.qq.com, 25; timeout -1
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2210)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:722)
at javax.mail.Service.connect(Service.java:342)
at org.springframework.mail.javamail.JavaMailSenderImpl.connectTransport(JavaMailSenderImpl.java:518)
at org.springframework.mail.javamail.JavaMailSenderImpl.doSend(JavaMailSenderImpl.java:437)
... 8 common frames omitted
Caused by: java.net.ConnectException: Connection timed out (Connection timed out)
在自己本地和公司服务器部署没问题,上到阿里云和腾讯云出现上面错误,有经过排查得知是:
- 阿里云出于安全考虑默认禁用25端口导致发邮件失败
解决方案
更改端口465ssl方式发送
spring:
mail:
host: smtp.qq.com
port: 465
username: 792750228@qq.com
#QQ邮箱的授权码
password: nybgwcppvgekbdjg
#默认协议smtp
#protocol: smtp
#默认UTF-8
#default-encoding: UTF-8
properties:
mail:
smtp:
socketFactory:
port: 465
class: javax.net.ssl.SSLSocketFactory
fallback: false
auth: true
starttls:
enable: true
required: true