SpringBoot:SpringBoot集成E-mail邮件发送功能

前言

  做项目时有个需求:用公司邮箱给客户发送邮件通知,然后上网冲浪找到一些不错的文章,通过优化并实现功能后,写这篇文章记录一下,也提供给大家做参考。

前期准备

在编写代码前,我们需要获取到一些信息用于后续邮件发送功能,需要获取的信息为:协议服务器地址、邮件发送协议、客户端授权码。

名词说明

什么是邮件协议?

  邮件协议主要有三种:POP3、IMAP、SMTP。简单来说,POP3和IMAP是用来从服务器上下载邮件的。SMTP适用于发送或中转信件时找到下一个目的地,所以我们发送邮件应该使用SMTP协议。

什么是协议服务器?

  邮箱协议服务器是托管用户电子邮件的服务器,负责接收、存储、发送和转发电子邮件。不同厂家的邮箱,对应的邮箱服务器也不一样。

什么是邮箱客户端授权码?

  邮箱客户端授权码(有的叫:客户端专用密码)是为了避免邮箱密码被盗后,盗号者通过客户端登录邮箱而设计的安防功能。这里我们获取邮箱客户端授权码就可以通过它发送邮件。

QQ邮箱获取信息

协议与服务器配置

IMAP 协议

  接收邮件服务器:imap.qq.com ,使用 SSL,端口号 993

POP3 协议

  接收邮件服务器:pop.qq.com ,使用 SSL,端口号 110

SMTP 协议

  发送邮件服务器:smtp.qq.com ,使用 SSL,端口号 465

客户端授权码获取

注意授权码获取后只显示一次,所以显示后就要复制,不然就再申请一次

网易邮箱获取信息

协议与服务器配置

IMAP 协议

  接收邮件服务器:imap.163.com ,使用 SSL,端口号 993

POP3 协议

  接收邮件服务器:pop.163.com ,使用 SSL,端口号 995

SMTP 协议

  发送邮件服务器:smtp.163.com ,使用 SSL,端口号 465

客户端授权码获取

注意授权码获取后只显示一次,所以显示后就要复制,不然就再申请一次

企业微信邮箱获取信息

协议与服务器配置

IMAP 协议

  接收邮件服务器:imap.exmail.qq.com ,使用 SSL,端口号 993

POP3 协议

  接收邮件服务器:pop.exmail.qq.com ,使用 SSL,端口号 995

SMTP 协议

  发送邮件服务器:smtp.exmail.qq.com ,使用 SSL,端口号 465

Exchange 协议

  服务器:ex.exmail.qq.com

客户端授权码获取

注意授权码获取后只显示一次,所以显示后就要复制,不然就再申请一次

引入依赖

<!--    邮箱依赖包    -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!--   freemarker页面渲染依赖包   -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

配置文件 

spring:
  # 邮件配置
  mail:
    default-encoding: utf-8
    # 协议服务器地址
    host: smtp.exmail.qq.com
    # SSL端口
    #prot: 465
    # 发送协议, 如果配置SSL端口,这里的发送协议改为 smtps
    protocol: smtp
    # 发送方的邮箱地址
    username: lsjgas@dsld.com
    # 授权码 (非邮箱密码)
    password: 9buX4WBhfcwjksdkxaf
  # HTML模板配置
  freemarker:
    cache: false # 缓存配置 开发阶段应该配置为false 因为经常会改
    suffix: .html # 模版后缀名 默认为ftl
    charset: UTF-8 # 文件编码
    template-loader-path: classpath:/templates/  # 存放模板的文件夹,以resource文件夹为相对路径

 编写工具类

 

 项目结构:

  EMailProperties:邮件配置类

  EMailUtils:邮件工具类

  email.html:HTML模板文件

 邮件配置类

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "spring.mail")
public class EMailProperties {
    // 字符集编码
    private String defaultEncoding;
    // 协议服务器地址
    private String host;
    // (发送方)邮箱账号
    private String username;
    // 授权码
    private String password;

    public String getDefaultEncoding() {
        return defaultEncoding;
    }

    public void setDefaultEncoding(String defaultEncoding) {
        this.defaultEncoding = defaultEncoding;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

邮件工具类

import com.higentec.common.utils.file.FileUtils;
import com.higentec.email.config.EMailProperties;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import io.micrometer.common.util.StringUtils;
import jakarta.annotation.Resource;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import java.io.IOException;
import java.util.Map;

@Component
@RequiredArgsConstructor
public class EMailUtils {
    // 邮箱配置类
    private final EMailProperties eMailProperties;
    // 邮箱发送类
    @Resource
    private JavaMailSender javaMailSender;
    // 页面模板渲染配置类
    private final FreeMarkerConfigurer freeMarkerConfigurer;

    /**
     * 发送文本邮件
     *
     * @param title       标题
     * @param text        文本
     * @param targetMails 目标邮箱
     */
    public void sendText(String title, String text, String... targetMails) {
        sendText(title, text, null, targetMails);
    }

    /**
     * 发送文本邮件
     *
     * @param title       标题
     * @param text        文本
     * @param Cc          抄送人
     * @param targetMails 目标邮箱
     */
    @SneakyThrows(Exception.class)
    public void sendText(String title, String text, String Cc, String... targetMails) {
        SimpleMailMessage message = new SimpleMailMessage();
        // 发件人邮箱地址
        message.setFrom(eMailProperties.getUsername());
        // 收件人邮箱地址数组(可实现批量发送)
        message.setTo(targetMails);
        // 邮箱标题
        message.setSubject(title);
        // 邮箱内容
        message.setText(text);
        if (StringUtils.isNotEmpty(Cc)) {
            // 抄送人邮箱地址
            message.setCc(Cc);
        }
        // 发送邮件
        javaMailSender.send(message);
    }

    /**
     * 发送HTML超文本邮件
     *
     * @param title       标题
     * @param templateName        模板名称(带后缀)
     * @param model        使用Map作为数据模型,定义属性和值
     * @param targetMails 目标邮箱
     */
    public void sendHtmlTemplate(String title, String templateName, Map<String, Object> model, String... targetMails) {
        String html = templateHtml(templateName, model);
        sendHtmlText(title, html, null, targetMails);
    }

    /**
     * 发送HTML超文本邮件
     *
     * @param title       标题
     * @param html        HTML超文本
     * @param targetMails 目标邮箱
     */
    public void sendHtmlText(String title, String html, String... targetMails) {
        sendHtmlText(title, html, null, targetMails);
    }

    /**
     * 发送HTML超文本邮件
     *
     * @param title       标题
     * @param html        HTML超文本
     * @param Cc          抄送人
     * @param targetMails 目标邮箱
     */
    @SneakyThrows(MessagingException.class)
    public void sendHtmlText(String title, String html, String Cc, String... targetMails) {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper mimeHelper = new MimeMessageHelper(message, true);
        // 发件人邮箱地址
        mimeHelper.setFrom(eMailProperties.getUsername());
        // 收件人邮箱地址数组(可实现批量发送)
        mimeHelper.setTo(targetMails);
        // 邮箱标题
        mimeHelper.setSubject(title);
        // 邮箱内容, true代表是html内容
        mimeHelper.setText(html, true);
        if (StringUtils.isNotEmpty(Cc)) {
            // 抄送人邮箱地址
            mimeHelper.setCc(Cc);
        }
        // 发送邮件
        javaMailSender.send(message);
    }

    /**
     * 发送携带附件文件的邮件
     *
     * @param title       标题
     * @param text        文本
     * @param fileMap     附件集合
     * @param targetMails 目标邮箱
     */
    public void sendAttachFileMail(String title, String text, Map<String, String> fileMap, String... targetMails) {
        sendAttachFileMail(title, text, null, fileMap, targetMails);
    }

    /**
     * 发送携带附件文件的邮件
     *
     * @param title       标题
     * @param text        文本
     * @param Cc          抄送人
     * @param fileMap     附件集合
     * @param targetMails 目标邮箱
     */
    @SneakyThrows(MessagingException.class)
    public void sendAttachFileMail(String title, String text, String Cc, Map<String, String> fileMap, String... targetMails) {
        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
        // 构建一个带附件的邮件对象
        MimeMessageHelper mimeHelper = new MimeMessageHelper(mimeMessage, true);
        // 发件人邮箱地址
        mimeHelper.setFrom(eMailProperties.getUsername());
        // 收件人邮箱地址数组(可实现批量发送)
        mimeHelper.setTo(targetMails);
        // 邮箱标题
        mimeHelper.setSubject(title);
        // 邮箱内容
        mimeHelper.setText(text);
        for (String filename : fileMap.keySet()) {
            //第一个参数是文件名称,第二个参数是文件流
            mimeHelper.addAttachment(filename, FileUtils.urlNetToMultipartFile(filename, fileMap.get(filename)));
        }
        if (StringUtils.isNotEmpty(Cc)) {
            // 抄送人邮箱地址
            mimeHelper.setCc(Cc);
        }
        javaMailSender.send(mimeMessage);
    }

    /**
     * 模板解析方法,解析出一个String的html返回
     * @param templateName 模板文件名称(带后缀)
     * @param model 使用Map作为数据模型,定义属性和值
     * @return
     */
    @SneakyThrows({IOException.class, TemplateException.class})
    public String templateHtml(String templateName, Map<String, Object> model) {
        // 获得模板
        Template template = freeMarkerConfigurer.getConfiguration().getTemplate(templateName);
        // 传入数据模型到模板,替代模板中的占位符,并将模板转化为html字符串
        String templateHtml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model);
        // 该方法本质上还是发送html邮件,调用之前发送html邮件的方法
        return templateHtml;
    }

}

文件流工具类

public class FileUtils {
    /**
     * 解析在线图片路径转换为 MultipartFile 对象
     * @param fileName 携带后缀的文件名称
     * @param urlNet 文件在线访问链接(minio永久访问链接)
     * @return
     */
    public static MultipartFile urlNetToMultipartFile(String fileName, String urlNet) {
        try {
            // 将在线图片地址转换为URL对象
            URL url = new URL(urlNet);
            // 打开URL连接
            URLConnection connection = url.openConnection();
            // 转换为HttpURLConnection对象
            HttpURLConnection httpURLConnection = (HttpURLConnection) connection;
            // 获取输入流
            InputStream inputStream = httpURLConnection.getInputStream();
            // 读取输入流中的数据,并保存到字节数组中
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                byteArrayOutputStream.write(buffer, 0, bytesRead);
            }
            // 将字节数组转换为字节数组
            byte[] bytes = byteArrayOutputStream.toByteArray();
            // 创建ByteArrayInputStream对象,将字节数组传递给它
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            String suffix = fileName.split(".")[1];
            FILE_CONTENT_TYPE content_type = FILE_CONTENT_TYPE.parseContentType(suffix);
            // 创建MultipartFile对象,将ByteArrayInputStream对象作为构造函数的参数
            MultipartFile multipartFile = new MultipartFileDto("file", fileName, content_type.getContentType(), byteArrayInputStream);
            return multipartFile;
        }catch (IOException ex){
            ex.printStackTrace();
            throw new UtilException("附件无效");
        }
    }
}

HTML模板文件

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no,
           initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>邮件发送</title>
</head>
<body>
    <div>
        <div align="center">
            <div class="open_email" style="margin-left: 8px; margin-top: 8px; margin-bottom: 8px; margin-right: 8px;">
                <div>
                    <br>
                    <span class="genEmailContent">
                        <div id="cTMail-Wrap"
                             style="word-break: break-all;box-sizing:border-box;text-align:center;min-width:320px; max-width:660px; border:1px solid #f6f6f6; background-color:#f7f8fa; margin:auto; padding:20px 0 30px; font-family:'helvetica neue',PingFangSC-Light,arial,'hiragino sans gb','microsoft yahei ui','microsoft yahei',simsun,sans-serif">
                            <div class="main-content" style="">
                                <table style="width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse">
                                    <tbody>
                                    <tr style="font-weight:300">
                                        <td style="width:3%;max-width:30px;"></td>
                                        <td style="max-width:600px;">
                                            <!-- {{--   LOGO  --}} -->
                                            <div id="cTMail-logo" style="width:92px; height:25px;">
                                                <!-- {{--   替换跳转链接  --}} -->
                                                <a href="">
                                                    <!-- {{--   替换LOGO图片  --}} -->
                                                    <img border="0" src="https://imgcache.qq.com/open_proj/proj_qcloud_v2/mc_2014/cdn/css/img/mail/logo-pc.png"
                                                         style="width:92px; height:25px;display:block">
                                                </a>
                                            </div>
                                            <!-- {{--   页面上边的蓝色分割线  --}} -->
                                            <p style="height:2px;background-color: #00a4ff;border: 0;font-size:0;padding:0;width:100%;margin-top:20px;"></p>

                                            <div id="cTMail-inner" style="background-color:#fff; padding:23px 0 20px;box-shadow: 0px 1px 1px 0px rgba(122, 55, 55, 0.2);text-align:left;">
                                                <table style="width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse;text-align:left;">
                                                    <tbody>
                                                    <!-- {{--   第一个单元格  --}} -->
                                                    <tr style="font-weight:300">
                                                        <!-- {{--   左侧表格,设置左边距用的  --}} -->
                                                        <td style="width:3.2%;max-width:30px;"></td>
                                                        <!-- {{--   中间表格,正文使用  --}} -->
                                                        <td style="max-width:480px;text-align:left;">
                                                            <!-- {{--   以下是正文 --}} -->
                                                            <!-- {{--   可以是标题  --}} -->
                                                            <h1 id="cTMail-title" style="font-size: 20px; line-height: 36px; margin: 0px 0px 22px;">
                                                                【XX平台】欢迎注册XXXXXX
                                                            </h1>

                                                            <p id="cTMail-userName" style="font-size:14px;color:#333; line-height:24px; margin:0;">
                                                                尊敬的${username},您好!
                                                            </p>

                                                            <p class="cTMail-content" style="line-height: 24px; margin: 6px 0px 0px; overflow-wrap: break-word; word-break: break-all;">
                                                                <span style="color: rgb(51, 51, 51); font-size: 14px;">
                                                                    欢迎注册巴拉巴拉一大堆话。
                                                                </span>
                                                            </p>

                                                            <p class="cTMail-content" style="line-height: 24px; margin: 6px 0px 0px; overflow-wrap: break-word; word-break: break-all;">
                                                                <span style="color: rgb(51, 51, 51); font-size: 14px;">完成注册,请点击下面按钮验证邮箱。
                                                                    <span style="font-weight: bold;">非本人操作可忽略。</span>
                                                                </span>
                                                            </p>

                                                            <!-- {{--   按钮  --}} -->
                                                            <p class="cTMail-content"
                                                               style="font-size: 14px; color: rgb(51, 51, 51); line-height: 24px; margin: 6px 0px 0px; word-wrap: break-word; word-break: break-all;">
                                                                <!-- {{--   下面替换成自己的链接  --}} -->
                                                                <a id="cTMail-btn" href="" title=""
                                                                   style="font-size: 16px; line-height: 45px; display: block; background-color: rgb(0, 164, 255); color: rgb(255, 255, 255); text-align: center; text-decoration: none; margin-top: 20px; border-radius: 3px;">
                                                                    点击此处验证邮箱
                                                                </a>
                                                            </p>

                                                            <p class="cTMail-content" style="line-height: 24px; margin: 6px 0px 0px; overflow-wrap: break-word; word-break: break-all;">
                                                                <span style="color: rgb(51, 51, 51); font-size: 14px;">
                                                                    <br>
                                                                    无法正常显示?请复制以下链接至浏览器打开:
                                                                    <br>
                                                                    <a href="" title=""
                                                                       style="color: rgb(0, 164, 255); text-decoration: none; word-break: break-all; overflow-wrap: normal; font-size: 14px;">
                                                                        这里是激活账号的链接
                                                                    </a>
                                                                </span>
                                                            </p>

                                                            <!-- {{--   来个署名  --}} -->
                                                            <dl style="font-size: 14px; color: rgb(51, 51, 51); line-height: 18px;">
                                                                <dd style="margin: 0px 0px 6px; padding: 0px; font-size: 12px; line-height: 22px;">
                                                                    <p id="cTMail-sender" style="font-size: 14px; line-height: 26px; word-wrap: break-word; word-break: break-all; margin-top: 32px;">
                                                                        此致
                                                                        <br>
                                                                        <strong>XXX团队</strong>
                                                                    </p>
                                                                </dd>
                                                            </dl>
                                                        </td>
                                                        <!-- {{--   右侧表格,设置右边距用的  --}} -->
                                                        <td style="width:3.2%;max-width:30px;"></td>
                                                    </tr>
                                                    </tbody>
                                                </table>
                                            </div>

                                            <!-- {{--   页面底部的推广  --}} -->
                                            <div id="cTMail-copy" style="text-align:center; font-size:12px; line-height:18px; color:#999">
                                                <table style="width:100%;font-weight:300;margin-bottom:10px;border-collapse:collapse">
                                                    <tbody>
                                                    <tr style="font-weight:300">
                                                        {<!-- {--   左,左边距  --}} -->
                                                        <td style="width:3.2%;max-width:30px;"></td>
                                                        <!-- {{--   中,正文  --}} -->
                                                        <td style="max-width:540px;">

                                                            <p style="text-align:center; margin:20px auto 14px auto;font-size:12px;color:#999;">
                                                                此为系统邮件,请勿回复。
                                                                <!-- {{--   可以加个链接  --}} -->
                                                                <a href=""
                                                                   style="text-decoration:none;word-break:break-all;word-wrap:normal; color: #333;" target="_blank">
                                                                    取消订阅
                                                                </a>
                                                            </p>

                                                            <!-- {{--   可以加个图片,公众号二维码之类的  --}} -->
                                                            <p id="cTMail-rights" style="max-width: 100%; margin:auto;font-size:12px;color:#999;text-align:center;line-height:22px;">
                                                                <img border="0" src="http://imgcache.qq.com/open_proj/proj_qcloud_v2/tools/edm/css/img/wechat-qrcode-2x.jpg"
                                                                     style="width:64px; height:64px; margin:0 auto;">
                                                                <br>
                                                                关注服务号,移动管理云资源
                                                                <br>
                                                                <img src="https://imgcache.qq.com/open_proj/proj_qcloud_v2/gateway/mail/cr.svg" style="margin-top: 10px;">
                                                            </p>
                                                        </td>
                                                        <!-- {{--   右,右边距  --}} -->
                                                        <td style="width:3.2%;max-width:30px;"></td>
                                                    </tr>
                                                    </tbody>
                                                </table>
                                            </div>
                                        </td>
                                        <td style="width:3%;max-width:30px;"></td>
                                    </tr>
                                    </tbody>
                                </table>
                            </div>
                        </div>
                    </span>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

 测试发送

/**
  * 测试发送邮件
  */
@Operation(summary = "测试发送邮件")
@GetMapping("/send/email")
public void sendEmail() {
    String title = "测试发送邮件";
    String templateName = "email.html";
    Map<String, Object> model = new HashMap<>();
    model.put("username", "客户01");
    String[] targetMails = new String[]{"dhdaf@hrfdtec.com"};
    eMailUtils.sendHtmlTemplate(title, templateName, model, targetMails);
}

posted @ 2024-11-19 17:57  怒吼的萝卜  阅读(279)  评论(0编辑  收藏  举报