2024 Java STMP 发送邮件工具类代码 支持腾讯/网易企业邮箱
2024-05-21
阅读 {{counts.readCount}}
评论 {{counts.commentCount}}
## 前言
2年前邮件写过一个类似的笔记
[java 通过 smtp 服务 给指定邮箱发送邮件 可包含附件](https://leanote.zzzmh.cn/blog/post/63688325da740500130169ab)
<br>
但是这段代码如今运行起来已经不方便了。
原因比较复杂,简单来说就是
这些年从 `java8` 发展到 `java21`
<br><br>
问题1:把 `javax` 部分代码逐渐迁移 `jakarta` 包下
并且不再是自带的,需要引入依赖
问题2:对于 `SSL` 的协议越来越严格,最低要求偏高,
比如 `JDK8` 最高支持到 `TLS1.2` ,从 `JDK11` 开始,最低已经是 `TLS1.2` 了
而网易企业邮箱强制开启 `SSL` ,否则会报错
<br><br>
对应的,现在执行**之前的邮件工具类代码**会出现2个问题
1. 最高只能支持 `JDK1.8` ,如果硬上 `JDK17` ,会因为 `SSL` 协议版本报安全错误,就算是 `JDK1.8`,新版 `OpenJDK` 都不是原生支持,要改 `java.security`,降低最低 `SSL` 版本要求
2. `SSL` 版本不符合最低 `TLS1.2` 要求,当你要开启 `TLS1.2` 的时候,就需要跟上最新版本的 `jakarta.mail` 依赖
<br><br>
综上所述,我百度了2天都没找到适合的代码,
CSDN全都是5年前的代码,互相来回抄,
2024年新发布的代码,还有在依赖sun或者javax的,
无奈,只能自己在原来代码的基础上小改一下,
结合外网的资料,缝缝补补又三年。
<br><br>
## 折腾
过程略过,直接上最终结果
这里接入的是网易企业邮箱,你直接套用到类似腾讯企业邮箱也是没问题的
maven依赖
`pom.xml`
```xml
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>jakarta.mail</artifactId>
<version>1.0.0</version>
</dependency>
```
工具类代码
`EmailUtils.java`
```java
package com.zzzmh.utils;
import com.sun.mail.util.MailSSLSocketFactory;
import jakarta.mail.*;
import jakarta.mail.internet.*;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.util.Properties;
/**
* 网易企业邮箱工具类
* @author zzzmh
* @date 2024年5月21日11:39:08
*/
public class EmailUtils {
// smtp协议一般不用改
private static final String protocol = "smtp";
// 这里的域名和端口根据你接入哪一家决定 我这是网易企业邮箱的
private static final String host = "smtp.qiye.163.com";
private static final String port = "994";
// 这里要改成你的账户名 一般是个邮箱地址
private static final String username = "xxx@xxx.com";
// 这里要改成你的密码 一般在邮箱设置里
private static final String password = "xxxxxxxxxxxxxxxx";
// 这里改成你的邮箱发送者名称 你自己起一个就行
private static final String personal = "xxxxxxxxxxxxxx";
// 身份验证
static class UserAuthenticator extends Authenticator {
String u = null;
String p = null;
public UserAuthenticator(String u, String p) {
this.u = u;
this.p = p;
}
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(u, p);
}
}
/** 请求参数 */
private static Properties getProperties() throws GeneralSecurityException {
Properties prop = new Properties();
// 协议
prop.setProperty("mail.transport.protocol", protocol);
// 域名
prop.setProperty("mail.smtp.host", host);
// 端口
prop.setProperty("mail.smtp.port", port);
// 使用smtp身份验证
prop.setProperty("mail.smtp.auth", "true");
// 使用SSL,企业邮箱必需!
MailSSLSocketFactory mailSSLSocketFactory = new MailSSLSocketFactory();
mailSSLSocketFactory.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.socketFactory", mailSSLSocketFactory);
prop.put("mail.smtp.ssl.enable", "true");
return prop;
}
/** 请求消息体 */
public static MimeMessage getMimeMessage(String subject, String content, String filePath, String fileName) throws GeneralSecurityException {
Properties prop = getProperties();
Session session = Session.getDefaultInstance(prop, new UserAuthenticator(username, password));
session.setDebug(false);
MimeMessage mimeMessage = new MimeMessage(session);
try {
// 发件人
if (StringUtils.isNotBlank(personal)) {
// 可以设置发件人的别名
mimeMessage.setFrom(new InternetAddress(username, personal));
} else {
// 如果不需要就省略
mimeMessage.setFrom(new InternetAddress(username));
}
// 主题
mimeMessage.setSubject(subject);
// 时间
mimeMessage.setSentDate(new Date());
// 容器类,可以包含多个MimeBodyPart对象
Multipart mp = new MimeMultipart();
// MimeBodyPart可以包装文本,图片,附件
MimeBodyPart body = new MimeBodyPart();
// HTML正文
body.setContent(content, "text/html; charset=UTF-8");
mp.addBodyPart(body);
// 添加图片&附件
if (StringUtils.isNotBlank(filePath)) {
MimeBodyPart mbp = new MimeBodyPart();
mbp.attachFile(filePath);
mbp.setFileName(MimeUtility.encodeWord(fileName));
mp.addBodyPart(mbp);
}
// 设置邮件内容
mimeMessage.setContent(mp);
mimeMessage.saveChanges();
return mimeMessage;
} catch (MessagingException | IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 发送邮件基本方法 (单发 含文件)
* 需要注意的是这里发送过程会很慢大约10~20秒
* 并且会阻塞线程,我这里就是这样设计的
* 如果你用在接口中,建议用@Async方案做成异步处理
*/
public static void sendEmail(String recipient, String subject, String content, String filePath, String fileName) {
try {
MimeMessage mimeMessage = getMimeMessage(subject, content, filePath, fileName);
// 单个收件人
mimeMessage.addRecipient(Message.RecipientType.TO, new InternetAddress(recipient));
Transport.send(mimeMessage);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 简化版
* @param recipient 对方邮箱地址
* @param subject 标题
* @param content 内容文本
*/
public static void sendEmail(String recipient, String subject, String content) {
sendEmail(recipient, subject, content, null, null);
}
public static void main(String[] args) {
sendEmail("admin@zzzmh.cn","测试标题","测试简单文本内容");
}
}
```
<br><br>
亲测大约10 ~ 20秒后发送成功
![截图](/api/file/getImage?fileId=664c1b57da7405001404eb03)
<br><br>
值得注意的是
这段代码我是特意设计成阻塞允许的,也就是从开始执行到发送成功,大约需要20秒,如果你用在Srpingboot的Controller中,请求会长达20秒以上,一般来说必须用异步处理一下。比如在方法上标注@Async。
## END