在企业级应用开发中,邮件发送是一个常见的功能需求,比如发送通知、报告、附件等。本文将详细介绍如何在 SpringBoot 项目中整合 JavaMailSender 实现邮件发送功能,特别是带附件的邮件发送,并针对实际应用场景进行优化。


一、JavaMailSender 简介

JavaMailSender 是 Spring 框架提供的一个用于发送电子邮件的接口,它是对 JavaMail API 的封装,提供了更简单、更方便的邮件发送功能。通过 JavaMailSender,我们可以轻松地发送简单文本邮件、HTML 邮件以及带附件的邮件。


二、邮件发送工具类实现

首先,我们需要创建一个邮件发送工具类,该工具类将封装所有邮件发送相关的功能。以下是完整的邮件发送工具类代码:

import com.sun.mail.util.MailSSLSocketFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;/*** 邮件发送工具类,提供发送带附件和不带附件的邮件功能*/
@Slf4j
public class SendEmailUtil {/*** 发送带附件的邮件* @param from 发件人邮箱地址* @param password 发件人邮箱密码或授权码* @param smtpHost 邮箱 SMTP 服务器地址* @param smtpPort 邮箱 SMTP 服务器端口* @param to 收件人邮箱地址,多个地址用分号(;)分隔* @param cc 抄送人的邮箱地址数组* @param subject 邮件主题* @param text 邮件正文* @param attachmentPath 附件文件的本地路径或远程URL*/public static boolean sendEmailWithAttachment(String from, String password, String smtpHost, int smtpPort, String to, String[] cc, String subject, String text, String attachmentPath) {File tempFile = null;try {// 如果附件路径是URL,则下载远程文件到本地临时文件if (attachmentPath != null && !attachmentPath.isEmpty()) {if (attachmentPath.toLowerCase().startsWith("http://") || attachmentPath.toLowerCase().startsWith("https://")) {tempFile = downloadRemoteFile(attachmentPath);} else {tempFile = new File(attachmentPath);if (!tempFile.exists()) {log.error("附件文件不存在: {}", attachmentPath);return false;}}}// 配置邮件发送器JavaMailSenderImpl mailSender = new JavaMailSenderImpl();mailSender.setHost(smtpHost);mailSender.setPort(smtpPort);mailSender.setUsername(from);mailSender.setPassword(password);// 设置邮件发送属性Properties props = mailSender.getJavaMailProperties();props.put("mail.smtp.auth", "true");props.put("mail.smtp.starttls.enable", "true");props.put("mail.smtp.timeout", "5000"); // 设置超时时间为5秒// 配置SSL加密MailSSLSocketFactory sf = new MailSSLSocketFactory();sf.setTrustAllHosts(true);props.put("mail.smtp.ssl.enable", "true");props.put("mail.smtp.ssl.socketFactory", sf);props.put("mail.smtp.starttls.required", "true");props.put("mail.smtp.ssl.protocols", "TLSv1.2");// 创建邮件消息MimeMessage message = mailSender.createMimeMessage();MimeMessageHelper helper = new MimeMessageHelper(message, true);helper.setFrom(from);// 处理多个收件人String[] receiveUserArr = to.split(";");helper.setTo(receiveUserArr);// 设置抄送人if (cc != null && cc.length > 0) {helper.setCc(cc);}helper.setSubject(subject);helper.setText(text);// 添加附件if (tempFile != null && tempFile.exists()) {helper.addAttachment(tempFile.getName(), tempFile);}// 发送邮件mailSender.send(message);log.info("邮件已成功从 {} 发送至: {}, 抄送人: {}", from, to, cc != null ? String.join(", ", cc) : "无");return true;} catch (Exception e) {log.error("发送带附件的邮件时出现错误", e);return false;} finally {// 确保临时文件被删除if (tempFile != null && tempFile.exists() && tempFile.getName().startsWith("attachment")) {try {tempFile.delete();} catch (Exception e) {log.error("删除临时文件失败", e);}}}}/*** 下载远程文件到本地临时文件*/public static File downloadRemoteFile(String remoteUrl) throws IOException {URL url = new URL(remoteUrl);File tempFile = File.createTempFile("attachment", getFileExtension(remoteUrl));try (InputStream in = url.openStream();FileOutputStream out = new FileOutputStream(tempFile)) {byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = in.read(buffer)) != -1) {out.write(buffer, 0, bytesRead);}}return tempFile;}/*** 从URL中提取文件扩展名*/private static String getFileExtension(String url) {if (url == null) return ".tmp";int lastDot = url.lastIndexOf('.');return lastDot >= 0 ? url.substring(lastDot) : ".tmp";}
}


三、邮件发送控制器实现

接下来,我们创建一个控制器类来处理邮件发送请求:

import org.springframework.web.bind.annotation.*;/*** 邮件发送控制器*/
@RestController
@RequestMapping("/api/email")
public class EmailController {/*** 发送带附件的邮件* @param toEmail 收件人邮箱地址,多个地址用分号(;)分隔* @param address 附件文件路径或URL* @return 发送结果*/@PostMapping("/send")public AjaxResult sendEmail(@RequestParam String toEmail, @RequestParam String address) {String from = "your_email@example.com"; // 发件人邮箱String password = "your_password_or_auth_code"; // 邮箱密码或授权码String smtpHost = "smtp.example.com"; // SMTP服务器地址int smtpPort = 465; // SMTP服务器端口String subject = "重要通知"; // 邮件主题String text = "这是一封带附件的重要通知邮件,请查收。"; // 邮件正文try {Boolean success = SendEmailUtil.sendEmailWithAttachment(from, password, smtpHost, smtpPort, toEmail, null, subject, text, address);if (success) {return AjaxResult.success("发送成功");} else {return AjaxResult.error("发送失败");}} catch (Exception e) {return AjaxResult.error("发送异常: " + e.getMessage());}}
}


四、优化说明

  1. 异常处理优化
  • 将方法声明中的throws Exception改为在方法内部捕获异常,提高代码健壮性
  • 添加了更详细的错误日志记录,便于问题排查
  1. 临时文件管理
  • 优化了临时文件的命名规则,根据 URL 自动推断文件扩展名
  • 在 finally 块中确保临时文件被删除,避免磁盘空间占用
  1. 远程文件处理
  • 增加了对 URL 路径的判断,支持直接使用远程文件作为附件
  • 提供了更通用的远程文件下载方法
  1. 配置优化
  • 调整了超时设置,避免长时间等待无响应的连接
  • 合并了相似方法,减少代码冗余
  1. 用户体验
  • 支持多个收件人,使用分号分隔
  • 提供更友好的返回信息


五、使用示例

下面是一个使用示例,展示如何调用邮件发送接口:

// 发送本地附件邮件
AjaxResult result = emailController.sendEmail("recipient1@example.com;recipient2@example.com", "/path/to/local/file.pdf");// 发送远程附件邮件
AjaxResult result = emailController.sendEmail("recipient@example.com", "https://example.com/report.pdf");

六、注意事项

  1. 请根据实际邮箱提供商配置正确的 SMTP 服务器地址和端口
  2. 建议将邮箱配置信息存储在配置文件中,而不是硬编码在代码中
  3. 使用授权码而非邮箱密码,提高账户安全性
  4. 处理大量邮件发送时,建议使用异步方式,避免阻塞主线程
  5. 注意邮件内容的合规性,避免发送垃圾邮件