Spring Boot 邮件发送(带附件)实战

使用的工具

工具 用途
Spring JavaMailSender 邮件发送核心,运行时按租户动态构建
EasyExcel 流式生成 Excel,写入 ByteArrayOutputStream,避免落盘
Spring @Async 自定义线程池异步处理,API 快速返回
ResourceBundle 国际化解耦,根据请求头 Accept-Language 切换 Excel 表头语言

设计思路

核心原则:同步做数据准备,异步做文件生成和投递

复制代码
Controller → Service(同步) → @Async(异步)
  API入口      参数校验          Excel构建
             分页拉取消息        i18n国际化
             查设备/模板/产品     SMTP投递
                ↓
           立即返回 200
  1. 同步阶段:参数校验 → 循环分页拉取聊天记录 → 查设备信息获取租户 → 查邮件模板 → 查产品名称
  2. 异步阶段 :根据语言加载 i18n 资源 → EasyExcel 生成字节流 → 动态构建 JavaMailSenderMimeMessageHelper 带附件发送
  3. 邮件模板内容由通知服务统一管理,支持变量替换(产品名、设备名、导出时间、格式)

关键代码

异步提交入口

java 复制代码
public BaseResult<?> sendChatHistoryEmail(SendChatHistoryDTO dto) {
    // 1. 参数校验
    // 2. 循环分页拉取 messages
    // 3. 查 device / template / productName
    // 4. 提交异步任务,立即返回
    buildAndSendEmailAsync(messages, productName, pk, dk,
            languageCode, device.getTenantId(), template, dto.getUserEmail());
    return BaseResult.success();
}

异步方法 --- Excel + 邮件

java 复制代码
@Async(ThreadPoolConfiguration.AIBIZ_EXECUTOR)
void buildAndSendEmailAsync(...) {
    // i18n 加载
    Locale locale = languageCode.contains("en") ? Locale.US : Locale.SIMPLIFIED_CHINESE;
    ResourceBundle bundle = ResourceBundle.getBundle("i18n.messages", locale);

    // EasyExcel 生成字节流
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ExcelWriter writer = EasyExcel.write(bos).build();
    WriteSheet sheet = EasyExcel.writerSheet(bundle.getString("EXPORT_SHEET_NAME"))
            .head(headList).build();
    writer.write(rowList, sheet);
    writer.finish();

    // 发送
    sendEmail(to, productName, pk, dk, exportTime, "excel",
            bos.toByteArray(), fileName + ".xlsx",
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            tenantId, template);
}

邮件发送核心

java 复制代码
private void sendEmail(...) throws MessagingException {
    // 按租户动态构建 MailSender
    String cfg = clientForNotice.queryNtcAccountInfo(NoticeModeEnum.EMAIL.name(), tenantId);
    EmailTunnelConfig config = JSON.parseObject(cfg, EmailTunnelConfig.class);
    JavaMailSender mailSender = buildMailSender(config);

    MimeMessageHelper helper = new MimeMessageHelper(mailSender.createMimeMessage(), true, "UTF-8");
    helper.setFrom(config.getFrom());
    helper.setTo(to);
    helper.setSubject(template.getTemplateName());
    helper.setText(StrUtil.format(template.getTemplateContent(), ...), true);

    // Lambda 延迟加载附件流
    helper.addAttachment(attachmentName, () -> new ByteArrayInputStream(fileBytes), contentType);
    mailSender.send(helper.getMimeMessage());
}

邮件发送者配置

java 复制代码
@Data
public class EmailTunnelConfig {
    private String nickName;  // 发件人昵称
    private String from;      // 发件人邮箱
    private String username;  // SMTP 账号
    private String password;  // SMTP 密码/授权码
    private String host;      // SMTP 服务器
    private Integer port;     // SMTP 端口
}

// 配置按租户存储,运行时动态拉取
String accountInfo = clientForNotice.queryNtcAccountInfo(NoticeModeEnum.EMAIL.name(), tenantId);
EmailTunnelConfig config = JSON.parseObject(accountInfo, EmailTunnelConfig.class);

// 动态构建 MailSender(多租户场景不能用单例 Bean)
JavaMailSenderImpl sender = new JavaMailSenderImpl();
sender.setHost(config.getHost());
sender.setPort(config.getPort());
sender.setUsername(config.getUsername());
sender.setPassword(config.getPassword());
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");

常见邮箱 SMTP 参考:QQ(smtp.qq.com:587)、163(smtp.163.com:25/465)、企业微信(smtp.exmail.qq.com:587)、阿里企业邮箱(smtp.mxhichina.com:465)、Outlook(smtp.office365.com:587)。

设计要点

  • 多租户邮箱JavaMailSender 运行时按 tenantId 动态构建,非单例 Bean
  • 拒绝策略CallerRunsPolicy 保证异步任务不丢失
  • 附件流 :Lambda 延迟创建 ByteArrayInputStream,避免大文件占满内存
  • i18nmessages_zh_CN.properties / messages_en_US.properties,通过 Accept-Language 请求头自动切换
相关推荐
爱刷碗的苏泓舒44 分钟前
网络通信入门:静态 IP、动态 IP、路由器、交换机与手机热点
网络·智能手机·路由器·网络通信·交换机·手机热点·终端调试
子非鱼94271 小时前
03-Flutter 鸿蒙实战 03:登录注册与本地会话持久化
网络·flutter·华为·harmonyos
明亮 Blog1 小时前
Wi-Fi 6/7的物联网“芯”机遇:高带宽低延时如何重塑智慧工厂的AGV物流革命
服务器·网络·数据库
不羁的木木2 小时前
《HarmonyOS技术精讲-NearLink Kit(星闪服务)》第10篇:多设备协同——构建星闪网络实现数据同步
网络·华为·harmonyos
清欢渡---2 小时前
HCIP-第五章vrrp
网络·网络安全·hcip
Inhand陈工2 小时前
当发电机装上“智慧大脑”:IG502+白鹰能源管家打造中东发电机远程监控与防盗方案
大数据·网络·物联网·阿里云·能源·边缘计算·信息与通信
CHANG_THE_WORLD3 小时前
TCP 四次挥手彻底解析:FIN、ACK、SEQ、半关闭与 TIME-WAIT
网络·网络协议·tcp/ip
楷哥爱开发12 小时前
如何使用 Claude Fable 5 进行网页抓取?2026最新实战教程
大数据·网络·人工智能
Sirius Wu12 小时前
OpenClaw Skill:Matplotlib 可视化技能 + 沙箱双层隔离完整详解
服务器·网络·人工智能·安全·ai·架构·aigc