springboot:java实现邮件及附件发送、HTML正文的三种方式(三)【附带源码】

2,124 阅读5分钟

0. 引言

邮件发送是我们日常开发中比较常见的功能,常用于预警信息提醒、统计数据定期发送等需求。一般该方法会由前人封装好,实际开发时只需要调用即可,但具体怎么实现的,如何从零实现邮件发送,这是我们要掌握的。

之前我们讲解了基于javax.mailorg.apache.commons.mail实现邮件发送, 今天继续讲解第三种方式基于spring-boot-starter-mail实现。

1. 环境准备

1.1 开发环境

以下演示基于当前项目使用的springboot版本,jdk基于1.8版本

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
            <version>2.3.7.RELEASE</version>
</dependency>

1.2 开启邮箱协议与授权

其次我们需要了解的是,程序要发送邮件,是需要一个邮箱账号的, 并且其账号需要开启SMTP邮件协议以及邮件授权码,并不是密码。

以下我们以QQ邮箱为例,示范其开启过程,其他邮箱大同小异。

1、登陆邮箱,点击设置,进入账户,下拉页面

在这里插入图片描述

2、找到POP3/IMAP/SMTP服务设置。这里我们可以开启POP3/SMTP或者IMAP/SMTP服务,两者的区别

在这里插入图片描述

3、点击开启后,会要求你发送短信验证

在这里插入图片描述

4、发送后,点击我已发送,然后会给你一个授权码,将该码保存下来,这就是我们需要的授权码。

在这里插入图片描述

5、其次我们需要获取到邮件服务器的smtp地址,比如我们这里用的是qq邮箱,其地址就是smtp.qq.com。对应类型邮箱的smtp地址直接百度即可。

1.3 常见的邮箱服务及端口

服务商smtp服务地址smtp服务端口pop3服务地址pop3服务端口
新浪 sina.comsmtp.sina.com.cn25pop3.sina.com.cn110
搜狐 sohu.comsmtp.sohu.com25pop3.sohu.com110
163 163.comsmtp.163.com25pop3.163.com110
QQ qq.comsmtp.qq.com25pop3.qq.com110
foxmail foxmail.comsmtp.foxmail.com25pop3.foxmail.com110
QQ企业邮箱 exmail.qq.comsmtp.exmail.qq.com995pop3.exmail.qq.com587/465

2. 实现

2.1 spring-boot-starter-mail实现

2.1.1 思路

1、我们观察spring-boot-starter-mail依赖包,发现其实内部使用jakarta.mail来实现

在这里插入图片描述

jakarta内部其实也有javax.mail的包。所以后续我们会发现很多用法与javax.mail类似

在这里插入图片描述

2.1.2 实操

1、引入依赖

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

2、修改配置文件application.yml

spring:
# spring-boot-starter-mail配置项
  mail:
    host: smtp.qq.com
    username: xxx@qq.com
    password: xxx
    default-encoding: UTF-8
    properties.mail.smtp.socketFactory.class: javax.net.ssl.SSLSocketFactory
    # 打印邮件发送过程,生产环境关闭
    properties.mail.debug: true

3、创建工具类,实现发送功能(注意这里已经将工具类声明为bean了,所以我们使用时要用依赖注入的形式调用)

/**
 * @author benjamin_5
 * @Description spring-boot-starter-mail邮件工具类
 * @date 2022/10/5
 */
@Component
@AllArgsConstructor
public class EmailSpringUtil {

    private final JavaMailSender javaMailSender;
    private final MailProperties mailProperties;
    /**
     * 邮件发送
     * @param subject 邮件主题
     * @param content 邮件内容
     * @param contentIsHtml 内容是否为html格式
     * @param fromMailPersonalName 发件人昵称
     * @param toMail 收件人邮箱
     * @param ccMail 抄送人邮箱
     * @param bccMail 秘密抄送人邮箱
     * @param fileNames 文件名(本地路径)
     * @throws GeneralSecurityException
     * @throws UnsupportedEncodingException
     * @throws MessagingException
     */
    public void sendEmail(String subject, String content,boolean contentIsHtml, String fromMailPersonalName,
                                  String toMail, String ccMail, String bccMail, List<String> fileNames) throws MessagingException, UnsupportedEncodingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message,true);
        helper.setFrom(mailProperties.getUsername(),fromMailPersonalName);
        helper.setTo(toMail);
        if(!ObjectUtils.isEmpty(ccMail)){
            helper.setCc(ccMail);
        }
        if(!ObjectUtils.isEmpty(bccMail)){
            helper.setBcc(bccMail);
        }
        helper.setSubject(subject);
        helper.setText(content,contentIsHtml);
        // 设置附件(注意这里的fileName必须是服务器本地文件名,不能是远程文件链接)
        if(!CollectionUtils.isEmpty(fileNames)){
            for (String fileName : fileNames) {
                FileDataSource fileDataSource = new FileDataSource(fileName);
                helper.addAttachment(fileDataSource.getName(),fileDataSource);
            }
        }
        javaMailSender.send(message);
    }
}

4、调用测试,我们创建一个接口来模拟测试

@RestController
@AllArgsConstructor
public class EmailController {

    private final EmailSpringUtil emailSpringUtil;

    @GetMapping("sendSpringEmail")
    public void sendSpringEmail(){
        String subject = "这是一个测试标题";
        String html = "<h1>统计数据如下所示:</h1>" +
                "<table border=\"1\">\n" +
                "  <tr>\n" +
                "    <th>月度销售额</th>\n" +
                "    <th>年度销售额</th>\n" +
                "  </tr>\n" +
                "  <tr>\n" +
                "    <td>10000</td>\n" +
                "    <td>2000000</td>\n" +
                "  </tr>\n" +
                "</table>";
        String toMail = "wuhanxue5@sina.com";
        String ccMail = "wuhanxue5@163.com";
        String fileName = "/Users/wuhanxue/Downloads/供应商接口参数.xlsx";
        try {
            emailSpringUtil.sendEmail(subject,html,true,"邮件提醒系统",toMail,ccMail,null, Arrays.asList(fileName));
        } catch (MessagingException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

4、浏览器访问该接口http://localhost:8080/sendSpringEmail

邮件接收成功,附件和HTML正文也显示正常

在这里插入图片描述

查看抄送邮箱,接收正常

在这里插入图片描述

补充问题

如果出现接收到的附件为.bin格式,这是因为附件名称过长导致,mime.mail中的参数splitlongparameters默认为 true,当附件名过长时,他会自动截取,就会导致我们接收到的附件格式变成.bin形式的。

要解决该问题就需要将其设置为false。于是我们创建一个启动执行类来单独设置

@Configuration
public class EmailToLongConfig {

    @PostConstruct
    private void init(){
        // 解决邮件附件名称太长会自动截取,导致附件变成.bin格式问题
        System.setProperty("mail.mime.splitlongparameters","false");
    }
}

当然我们也可以将System.setProperty("mail.mime.splitlongparameters","false");放到邮件发送的方法中去。

当然如果需求允许,也可以设置一个短一点的附件名来规避该问题

源码地址

以上演示的源码可以在如下地址中下载

git源码地址

总结

从代码可以看出spring-boot-starter-mailjavax.mail的实现类似,都是通过MimeMessageHelper类实现的

至此我们已经讲解完三种实现邮件发送的方法了,实际上邮件发送功能实现非常简单,毕竟我们只是在前人做好的组件上开发,已经站在了巨人的肩膀上。之前演示的代码大家也可以直接应用到生产中,但一定不要盲目的复制粘贴,理解,自己一行一行的复写一遍代码,这是千万不能省的!