领先的免费Web技术教程,涵盖HTML到ASP.NET

网站首页 > 知识剖析 正文

Spring Boot 邮件发送功能实现_spring boot email

nixiaole 2025-09-13 07:27:44 知识剖析 2 ℃

Spring Boot 邮件发送功能实现

一、功能设计

本文将实现一个完整的 Spring Boot 邮件发送系统,包含前端与后端两部分,功能如下:

  • 简洁的邮件发送表单界面
  • 支持 收件人、主题、内容 填写
  • 支持 附件上传(多文件)
  • 响应式设计,适配不同设备
  • 发送状态实时反馈
  • 可扩展:邮件模板、异步发送、日志记录

二、前端实现

前端使用 Bootstrap 5,包含收件人、主题、内容、附件上传等表单项,并提供 拖拽上传文件发送状态提示

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Spring Boot 邮件发送系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body { background-color: #f5f8fa; padding: 20px; }
        .email-card { box-shadow: 0 0 15px rgba(0, 0, 0, 0.1); border-radius: 10px; border: none; }
        .card-header { background-color: #0d6efd; color: white; border-radius: 10px 10px 0 0 !important; }
        .attachment-area { border: 2px dashed #ccc; border-radius: 5px; padding: 15px; text-align: center; background-color: #f8f9fa; cursor: pointer; }
        .attachment-area:hover { background-color: #e9ecef; }
        .btn-send { background-color: #0d6efd; border: none; padding: 10px 20px; font-weight: bold; }
        .btn-send:hover { background-color: #0b5ed7; }
    </style>
</head>
<body>
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card email-card">
                    <div class="card-header">
                        <h3 class="card-title mb-0">发送邮件</h3>
                    </div>
                    <div class="card-body">
                        <form id="emailForm" enctype="multipart/form-data">
                            <div class="mb-3">
                                <label for="to" class="form-label">收件人</label>
                                <input type="email" class="form-control" id="to" required multiple placeholder="多个收件人用逗号分隔">
                            </div>
                            <div class="mb-3">
                                <label for="subject" class="form-label">主题</label>
                                <input type="text" class="form-control" id="subject" required placeholder="邮件主题">
                            </div>
                            <div class="mb-3">
                                <label for="content" class="form-label">内容</label>
                                <textarea class="form-control" id="content" rows="5" required placeholder="输入邮件内容..."></textarea>
                            </div>
                            <div class="mb-3">
                                <label class="form-label">附件</label>
                                <div class="attachment-area" onclick="document.getElementById('attachments').click()">
                                    <div class="text-muted">
                                        <p>点击选择文件或拖拽文件到此处</p>
                                        <small>支持多个文件</small>
                                    </div>
                                </div>
                                <input type="file" id="attachments" multiple style="display:none" onchange="handleFileSelect(event)">
                                <div id="attachmentList" class="mt-2"></div>
                            </div>
                            <button type="submit" class="btn btn-primary btn-send w-100">
                                <span id="sendText">发送邮件</span>
                                <div id="loadingSpinner" class="spinner-border spinner-border-sm" role="status" style="display:none;">
                                    <span class="visually-hidden">发送中...</span>
                                </div>
                            </button>
                        </form>
                        <div id="alertBox" class="mt-3"></div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script>
        function handleFileSelect(event) {
            const files = event.target.files;
            const attachmentList = document.getElementById('attachmentList');
            attachmentList.innerHTML = '';
            if (files.length > 0) {
                const list = document.createElement('ul');
                list.className = 'list-group';
                for (let i = 0; i < files.length; i++) {
                    const listItem = document.createElement('li');
                    listItem.className = 'list-group-item d-flex justify-content-between align-items-center';
                    listItem.innerHTML = `${files[i].name} <span class="badge bg-primary rounded-pill">${(files[i].size/1024).toFixed(1)} KB</span>`;
                    list.appendChild(listItem);
                }
                attachmentList.appendChild(list);
            }
        }

        document.getElementById('emailForm').addEventListener('submit', function(e) {
            e.preventDefault();
            const sendButton = document.querySelector('button[type="submit"]');
            const sendText = document.getElementById('sendText');
            const spinner = document.getElementById('loadingSpinner');

            sendText.textContent = '发送中...';
            spinner.style.display = 'inline-block';
            sendButton.disabled = true;

            const formData = new FormData();
            formData.append('to', document.getElementById('to').value);
            formData.append('subject', document.getElementById('subject').value);
            formData.append('content', document.getElementById('content').value);

            const attachments = document.getElementById('attachments').files;
            for (let i = 0; i < attachments.length; i++) {
                formData.append('attachments', attachments[i]);
            }

            fetch('/api/sendEmail', { method: 'POST', body: formData })
                .then(res => res.json())
                .then(data => {
                    showAlert(data.success ? '邮件发送成功!' : '发送失败:' + data.message, data.success ? 'success' : 'danger');
                    if (data.success) document.getElementById('emailForm').reset();
                })
                .catch(err => showAlert('错误:' + err.message, 'danger'))
                .finally(() => {
                    sendText.textContent = '发送邮件';
                    spinner.style.display = 'none';
                    sendButton.disabled = false;
                });
        });

        function showAlert(message, type) {
            document.getElementById('alertBox').innerHTML = `<div class="alert alert-${type} alert-dismissible fade show">${message}<button type="button" class="btn-close" data-bs-dismiss="alert"></button></div>`;
        }
    </script>
</body>
</html>

三、后端实现

1. Maven 依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
</dependencies>

2. 配置文件 (application.yml)

spring:
  mail:
    host: smtp.qq.com   # SMTP 服务器地址
    username: your-email@qq.com
    password: your-app-password  # 授权码,不是邮箱密码
    properties:
      mail.smtp.auth: true
      mail.smtp.starttls.enable: true
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 20MB

3. 控制器 (EmailController.java)

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.mail.internet.MimeMessage;
import java.util.HashMap;
import java.util.Map;

@RestController
public class EmailController {
    @Autowired
    private JavaMailSender mailSender;

    @PostMapping("/api/sendEmail")
    public Map<String, Object> sendEmail(
            @RequestParam String to,
            @RequestParam String subject,
            @RequestParam String content,
            @RequestParam(required = false) MultipartFile[] attachments) {
        Map<String, Object> resp = new HashMap<>();
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);

            helper.setTo(to.split("\\s*,\\s*"));
            helper.setSubject(subject);
            helper.setText(content, true);

            if (attachments != null) {
                for (MultipartFile file : attachments) {
                    if (!file.isEmpty()) {
                        helper.addAttachment(file.getOriginalFilename(),
                                new ByteArrayResource(file.getBytes()));
                    }
                }
            }

            mailSender.send(message);
            resp.put("success", true);
        } catch (Exception e) {
            resp.put("success", false);
            resp.put("message", e.getMessage());
        }
        return resp;
    }
}

4. 启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EmailApplication {
    public static void main(String[] args) {
        SpringApplication.run(EmailApplication.class, args);
    }
}

四、使用说明

  1. 修改 application.yml 中的邮箱配置。
  2. 启动项目,访问 http://localhost:8080。
  3. 填写收件人、主题、正文,上传附件。
  4. 点击 发送 按钮即可完成邮件发送。

五、注意事项

  • 必须在邮箱中开启 SMTP 服务,并使用授权码。
  • 常见邮箱配置:
    • QQ 邮箱:smtp.qq.com,端口 465/587
    • 163 邮箱:smtp.163.com
    • Gmail:smtp.gmail.com
  • 上传文件大小受 spring.servlet.multipart.max-file-size 限制。

六、扩展与优化

  1. 安全性
  • 使用环境变量存储邮箱账号密码,而不是写死在配置文件。
  1. 异步发送
  • 使用 @Async 或消息队列,避免阻塞主线程:
@Async public void sendMailAsync(...) { ... }
  1. 模板美化
  • 使用 Thymeleaf 渲染 HTML 邮件模板,发送更美观的通知。
  1. 发送记录
  • 在数据库中记录邮件发送历史,方便查询与追踪。

以上就是一个完整的 Spring Boot 邮件发送功能实现,前后端一体化,可直接运行,也便于扩展成企业级邮件通知系统。


Tags:

最近发表
标签列表