在现代应用程序中,邮件发送功能是一个非常常见的需求,无论是用户注册验证、密码重置还是系统通知,都需要用到邮件发送功能。本文将详细介绍如何使用 Java 实现邮件发送功能,包括发送简单文本邮件、HTML 格式邮件以及带附件的邮件。
一、准备工作
1.1 所需依赖库
Java 发送邮件主要依赖于 JavaMail API 和 Java Activation Framework (JAF)。在 Maven 项目中,我们可以通过以下依赖引入:
<!-- JavaMail API -->
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<!-- Java Activation Framework -->
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
对于 Gradle 项目:
implementation 'com.sun.mail:javax.mail:1.6.2'
implementation 'javax.activation:activation:1.1.1'
1.2 邮件服务器配置
发送邮件需要用到 SMTP 服务器,常见的免费 SMTP 服务器有:
- Gmail: smtp.gmail.com (端口: 587 或 465)
- QQ 邮箱: smtp.qq.com (端口: 587)
- 163 邮箱: smtp.163.com (端口: 25)
注意:使用第三方邮箱服务时,通常需要开启 SMTP 服务,并可能需要生成专用密码(如 Gmail 的应用专用密码)。
二、邮件发送核心类介绍
JavaMail API 中几个核心的类:
- Session: 表示邮件会话,是所有邮件操作的基础
- Message: 表示邮件消息,是邮件内容的容器
- Address: 表示邮件地址
- Transport: 负责发送邮件
- Store: 负责接收邮件(本文不涉及)
- MimeMessage: 表示 MIME 类型的邮件消息
- MimeMultipart: 表示多部分内容的容器,用于构建带附件或 HTML 内容的邮件
三、实现邮件发送功能
3.1 邮件发送工具类
首先,我们创建一个邮件发送的工具类,封装通用的配置和发送逻辑:
import javax.mail.*;
import javax.mail.internet.*;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import java.util.Properties;
/**
* 邮件发送工具类
*/
public class EmailSender {
// 邮件服务器SMTP地址
private String smtpHost;
// 邮件服务器SMTP端口
private int smtpPort;
// 发件人邮箱
private String username;
// 发件人密码或授权码
private String password;
/**
* 构造函数
* @param smtpHost SMTP服务器地址
* @param smtpPort SMTP服务器端口
* @param username 发件人邮箱
* @param password 发件人密码或授权码
*/
public EmailSender(String smtpHost, int smtpPort, String username, String password) {
this.smtpHost = smtpHost;
this.smtpPort = smtpPort;
this.username = username;
this.password = password;
}
/**
* 获取邮件会话
*/
private Session getSession() {
// 配置邮件服务器属性
Properties props = new Properties();
props.put("mail.smtp.host", smtpHost);
props.put("mail.smtp.port", smtpPort);
props.put("mail.smtp.auth", "true"); // 需要身份验证
// 对于TLS加密的连接
if (smtpPort == 587) {
props.put("mail.smtp.starttls.enable", "true");
}
// 对于SSL加密的连接(如465端口)
else if (smtpPort == 465) {
props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
}
// 创建会话对象,设置身份验证
return Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
/**
* 发送简单文本邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param content 邮件内容
* @throws MessagingException 邮件发送异常
*/
public void sendTextEmail(String to, String subject, String content) throws MessagingException {
sendEmail(new String[]{to}, null, null, subject, content, "text/plain", null);
}
/**
* 发送HTML格式邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param htmlContent HTML内容
* @throws MessagingException 邮件发送异常
*/
public void sendHtmlEmail(String to, String subject, String htmlContent) throws MessagingException {
sendEmail(new String[]{to}, null, null, subject, htmlContent, "text/html", null);
}
/**
* 发送带附件的邮件
* @param to 收件人邮箱
* @param subject 邮件主题
* @param content 邮件内容
* @param attachmentPaths 附件路径数组
* @throws MessagingException 邮件发送异常
*/
public void sendEmailWithAttachment(String to, String subject, String content, String[] attachmentPaths)
throws MessagingException {
sendEmail(new String[]{to}, null, null, subject, content, "text/plain", attachmentPaths);
}
/**
* 发送邮件的通用方法
* @param to 收件人邮箱数组
* @param cc 抄送邮箱数组
* @param bcc 密送邮箱数组
* @param subject 邮件主题
* @param content 邮件内容
* @param contentType 内容类型,如"text/plain"或"text/html"
* @param attachmentPaths 附件路径数组,可为null
* @throws MessagingException 邮件发送异常
*/
public void sendEmail(String[] to, String[] cc, String[] bcc, String subject,
String content, String contentType, String[] attachmentPaths)
throws MessagingException {
// 获取会话
Session session = getSession();
// 开启调试模式,可以查看邮件发送的详细日志
session.setDebug(true);
// 创建邮件消息
MimeMessage message = new MimeMessage(session);
// 设置发件人
message.setFrom(new InternetAddress(username));
// 设置收件人
if (to != null && to.length > 0) {
Address[] toAddresses = new InternetAddress[to.length];
for (int i = 0; i < to.length; i++) {
toAddresses[i] = new InternetAddress(to[i]);
}
message.setRecipients(Message.RecipientType.TO, toAddresses);
}
// 设置抄送
if (cc != null && cc.length > 0) {
Address[] ccAddresses = new InternetAddress[cc.length];
for (int i = 0; i < cc.length; i++) {
ccAddresses[i] = new InternetAddress(cc[i]);
}
message.setRecipients(Message.RecipientType.CC, ccAddresses);
}
// 设置密送
if (bcc != null && bcc.length > 0) {
Address[] bccAddresses = new InternetAddress[bcc.length];
for (int i = 0; i < bcc.length; i++) {
bccAddresses[i] = new InternetAddress(bcc[i]);
}
message.setRecipients(Message.RecipientType.BCC, bccAddresses);
}
// 设置邮件主题
message.setSubject(subject);
// 创建多部分内容
Multipart multipart = new MimeMultipart();
// 创建消息体部分
BodyPart messageBodyPart = new MimeBodyPart();
messageBodyPart.setContent(content, contentType + "; charset=utf-8");
multipart.addBodyPart(messageBodyPart);
// 添加附件
if (attachmentPaths != null && attachmentPaths.length > 0) {
for (String filePath : attachmentPaths) {
BodyPart attachmentBodyPart = new MimeBodyPart();
DataSource source = new FileDataSource(filePath);
attachmentBodyPart.setDataHandler(new DataHandler(source));
// 处理中文附件名
String fileName = new String(filePath.substring(filePath.lastIndexOf("/") + 1)
.getBytes(), "ISO-8859-1");
attachmentBodyPart.setFileName(fileName);
multipart.addBodyPart(attachmentBodyPart);
}
}
// 将多部分内容设置为邮件内容
message.setContent(multipart);
// 发送邮件
Transport.send(message);
System.out.println("邮件发送成功!");
}
}
3.2 使用示例
下面是使用上述工具类发送不同类型邮件的示例:
import javax.mail.MessagingException;
/**
* 邮件发送示例
*/
public class EmailSenderExample {
public static void main(String[] args) {
// 配置邮件服务器信息(以Gmail为例)
String smtpHost = "smtp.gmail.com";
int smtpPort = 587;
String username = "your-email@gmail.com";
String password = "your-password-or-app-specific-password"; // 注意:对于Gmail,这里需要使用应用专用密码
// 创建邮件发送器
EmailSender emailSender = new EmailSender(smtpHost, smtpPort, username, password);
// 收件人邮箱
String toEmail = "recipient@example.com";
try {
// 1. 发送简单文本邮件
System.out.println("发送简单文本邮件...");
emailSender.sendTextEmail(toEmail, "测试简单文本邮件", "这是一封使用Java发送的简单文本邮件。");
// 2. 发送HTML格式邮件
System.out.println("\n发送HTML格式邮件...");
String htmlContent = "<h1>HTML邮件测试</h1>"
+ "<p>这是一封<span style='color: red;'>HTML格式</span>的邮件。</p>"
+ "<p>可以包含各种HTML标签,如链接:<a href='https://www.example.com'>示例网站</a></p>";
emailSender.sendHtmlEmail(toEmail, "测试HTML格式邮件", htmlContent);
// 3. 发送带附件的邮件
System.out.println("\n发送带附件的邮件...");
String[] attachments = {
"C:/test/file1.txt",
"C:/test/image.jpg"
};
emailSender.sendEmailWithAttachment(toEmail, "测试带附件的邮件",
"这是一封带附件的邮件,请查收。", attachments);
// 4. 发送更复杂的邮件(多个收件人、抄送、密送等)
System.out.println("\n发送复杂邮件...");
String[] toEmails = {toEmail, "another-recipient@example.com"};
String[] ccEmails = {"cc-recipient@example.com"};
String[] bccEmails = {"bcc-recipient@example.com"};
emailSender.sendEmail(toEmails, ccEmails, bccEmails,
"测试复杂邮件", "这封邮件有多个收件人、抄送和密送。",
"text/plain", null);
} catch (MessagingException e) {
e.printStackTrace();
System.out.println("邮件发送失败:" + e.getMessage());
}
}
}
3.3 不同邮件服务商的配置
不同邮件服务商的 SMTP 配置有所不同,以下是一些常见的配置:
// Gmail配置
String smtpHost = "smtp.gmail.com";
int smtpPort = 587; // 或465(SSL)
// QQ邮箱配置
String smtpHost = "smtp.qq.com";
int smtpPort = 587;
// 163邮箱配置
String smtpHost = "smtp.163.com";
int smtpPort = 25;
// Outlook配置
String smtpHost = "smtp.office365.com";
int smtpPort = 587;
四、常见问题及解决方案
4.1 授权问题
许多邮件服务商(如 Gmail)需要开启 "允许不够安全的应用" 或生成专用密码。对于 Gmail,推荐使用应用专用密码:
- 开启两步验证
- 生成应用专用密码
- 在代码中使用此专用密码
4.2 端口和加密方式
- 25 端口:通常使用 TLS 加密
- 587 端口:通常使用 STARTTLS 加密
- 465 端口:通常使用 SSL 加密
确保代码中的加密设置与端口匹配。
4.3 中文乱码问题
在设置邮件主题和内容时,确保指定了正确的字符集:
// 设置主题时指定字符集
message.setSubject(subject, "UTF-8");
// 设置内容时指定字符集
messageBodyPart.setContent(content, "text/html; charset=utf-8");
对于附件中文名称,需要特殊处理:
// 处理中文附件名
String fileName = new String(originalFileName.getBytes(), "ISO-8859-1");
attachmentBodyPart.setFileName(fileName);
4.4 防火墙或网络限制
有些网络环境可能会封锁 25、465、587 等端口,导致邮件发送失败。可以尝试更换端口或联系网络管理员。
五、在 Web 应用中集成
在 Web 应用中使用邮件发送功能时,建议:
- 将邮件配置信息放在配置文件中,而不是硬编码
- 使用线程池异步发送邮件,避免阻塞主线程
- 实现邮件发送失败的重试机制
- 记录邮件发送日志,便于问题排查
以下是一个简单的 Spring Boot 集成示例:
@Component
public class EmailService {
@Value("${spring.mail.host}")
private String smtpHost;
@Value("${spring.mail.port}")
private int smtpPort;
@Value("${spring.mail.username}")
private String username;
@Value("${spring.mail.password}")
private String password;
private EmailSender emailSender;
@PostConstruct
public void init() {
emailSender = new EmailSender(smtpHost, smtpPort, username, password);
}
@Async
public CompletableFuture<Void> sendAsyncEmail(String to, String subject, String content) {
return CompletableFuture.runAsync(() -> {
try {
emailSender.sendTextEmail(to, subject, content);
} catch (MessagingException e) {
// 处理异常,可能的重试逻辑
log.error("邮件发送失败", e);
}
});
}
}
六、总结
本文详细介绍了如何使用 Java 实现邮件发送功能,包括:
- 必要的依赖库和配置
- 核心类的作用和使用方法
- 完整的邮件发送工具类实现
- 发送不同类型邮件的示例
- 常见问题及解决方案
- 在 Web 应用中的集成建议
需要注意的是,不同邮件服务商有不同的限制和策略,在实际使用中可能需要根据具体情况进行调整。另外,对于生产环境的应用,建议实现更完善的错误处理和日志记录。