开发者社区> 美码师> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

补习系列(12)-springboot 与邮件发送

简介: [TOC] 一、邮件协议 在谈谈代码之前,先来了解下邮件的基本协议。 电子邮件协议是基于TCP层定义的,主要有下面几个: SMTP协议SMTP 是 Simple Mail Transfer Protocol 的简称,即简单邮件传输协议,是发送协议。
+关注继续查看

[TOC]

一、邮件协议

在谈谈代码之前,先来了解下邮件的基本协议。

电子邮件协议是基于TCP层定义的,主要有下面几个:

  • SMTP协议

SMTP 是 Simple Mail Transfer Protocol 的简称,即简单邮件传输协议,是发送协议。
它定义了一组从源地址到目的地址传输邮件的规范,并支持在传送过程中通过不同网络主机实现中转及传送。

  • POP3协议

POP3是 Post Office Protocol 3 的简称,属于接收协议,POP3是即POP(邮局协议)的第3个版本,也是因特网电子邮件的第一个离线协议。
它规定了终端如何接入远程的邮件服务器并下载电子邮件。

  • IMAP协议

IMAP的全称是 Internet Mail Access Protocol,即交互式邮件访问协议,是一种支持同步接收的协议。
该协议由斯坦福大学在1986年研发,目前是最流行的邮件收取功能协议。
开启IMAP功能之后,电子邮件客户端可同步接收服务端的邮件,无论在客户端还是服务端上的操作都会反馈到另一方,比如删除、标记等;
此外IMAP还支持只对选中的部分邮件进行收取,这在POP协议上是做不到的。

关于数据传输

大多人都知道,电子邮件的传输采用了Base64编码对邮件内容进行包装,这是一种基于64个可打印字符来表示二进制数据的方法。

如上是Base64编码的字符映射表,64个字符可对应6个bit位。
一个字节是8个bit位,那么3个字节刚好需要4个Base64的字符来表示,而3个字节(4个字符)也是Base64编码的最小单位,
在编码过程中对于不足的部分采用"="号来补齐,如下:

另外一个需要知道的协议是MIME(Multipurpose Internet Mail Extensions),即多用途互联网邮件扩展
在前面介绍SpringBoot-MiMe类型处理的文章中提到过,这是一种用来定义文档性质及格式的标准。
一段内容,是文本、图片、音频,还是二进制,都通过MIME类型来进行声明和解析。

常见的MIME

内容 后缀 MIME
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
PDF文档 .pdf application/pdf
Word文件 .word application/msword
PNG图像 .png image/png
GIF图形 .gif image/gif
JPEG图形 .jpg image/jpeg

二、SpringBoot 与邮件

SpringBoot 是一个脚手架,邮件功能其实是通过 JavaMail来实现的。
JavaMail是Java实现邮件收发功能的标准组件,其提供了一组简便的API来实现邮件处理,同时也支持各类认证协议。
这里不对JavaMail 做展开介绍,由于有了SpringBoot,实现一个邮件发送功能变得非常简单。

下面将展示几个例子,包括:

  • 使用springboot 发送文本邮件;
  • 如何发送带附件的邮件;
  • 如何使用 thymeleaf 发送模板邮件,支持HTML格式。

A. 添加依赖

spring-boot-starter-mail是一个封装了邮件功能的组件,依赖如下:

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-mail</artifactId>
 <version>${spring-boot.version}</version>
</dependency>

B. 配置文件

按下面的配置设置SMTP服务器、用户密码、及收发人信息

//smtp 服务器
spring.mail.host=smtp.qq.com
//smtp 端口
spring.mail.port=25
//发送用户名
spring.mail.username=xxx
//发送密码
spring.mail.password=xxx

//收发人
spring.mail.from=xxx@qq.com
spring.mail.to=xxx@qq.com

//启用鉴权
spring.mail.properties.mail.smtp.auth=true
//不使用tls
spring.mail.properties.mail.smtp.starttls.enable=false
spring.mail.properties.mail.smtp.starttls.required=false

C. 发送文本邮件

编写下面的代码,实现简单的文本发送:

@Service
public class SimpleMailSender implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(SimpleMailSender.class);

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private Environment environment;

    private void sendText() {
        String from = environment.getProperty("spring.mail.from");
        String to = environment.getProperty("spring.mail.to");

        SimpleMailMessage msg = new SimpleMailMessage();
        msg.setFrom(from);
        msg.setTo(to);

        msg.setSubject("first email from yourself");
        msg.setText("hello world!");

        this.mailSender.send(msg);
        logger.info("send text done");
    }

    @Override
    public void run(String... args) throws Exception {
        sendText();
    }

JavaMailSender、SimpleMailMessage 都是对JavaMail接口的封装,目的仅在于提供更简易的使用方式。
SimpleMailSender 继承了CommandLineRunner ,在SpringBoot启动时会触发sendText方法,
此时尝试启动SpringBoot应用,可以看到日志输出:

o.h.s.m.SimpleMailSender                 : send text done

此时检查收件箱,便可以看到对应的文本邮件。

D.发送附件

基于前面发送文本的例子,实现附件发送的代码如下:


    private void sendAttachment() throws MessagingException {
        String from = environment.getProperty("spring.mail.from");
        String to = environment.getProperty("spring.mail.to");

        // 使用Mime消息体
        MimeMessage message = mailSender.createMimeMessage();

        // multipart参数为true,表示需要发送附件
        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setFrom(from);
        helper.setTo(to);

        helper.setSubject("first file from yourself");
        helper.setText("check the file");

        //指定系统文件
        File file = new File("D:\\temp\\attachment.xlsx");
        FileSystemResource resource = new FileSystemResource(file);
        helper.addAttachment(file.getName(), resource);

        mailSender.send(message);

        logger.info("send attachment done");
    }

同样,启动应用并发送邮件后,在收件邮件中获得了附件:

E. 发送Html邮件

许多邮件都包含了丰富的文本样式,这是通过HTML邮件实现的。
对于此类场景的通用做法是使用模板来发送,应用程序只关注模型数据的传参即可。

SpringBoot 可利用 thymeleaf 页面引擎来实现HTML的模板,首先需要引入thymeleaf

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
 <version>${spring-boot.version}</version>
</dependency>

接着新建一个模板,
/src/main/resources/templates/mail/template.html

<html>
<body>

<h4 th:text="|Hi, ${customer}, these're your pets|"></h4>
<hr></hr>

<table>
  <tr>
    <th>name</th>
    <th>type</th>
    <th>age</th>
  </tr>
  <tr th:each="p : ${pets}">
    <td th:text="${p.name}"></td>
    <td th:text="${p.type}"></td>
    <td th:text="${p.age}"></td>
  </tr>
</table>

</body>
</html>

上面的模板中是一个宠物列表的页面(表格),宠物模型定义:

public static class Pet {

    private String name;
    private String type;
    private int age;

    public Pet(String name, String type, int age) {
        super();
        this.name = name;
        this.type = type;
        this.age = age;
    }
...

我们在发送邮件时,需要注入宠物列表数据
代码如下:

@Service
public class SimpleMailSender {
    /**
     * 日志工具
     */
    private static final Logger logger = LoggerFactory.getLogger(MailService.class);

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private TemplateEngine templateEngine;

    @Autowired
    private Environment environment;

    private void sendTemplateMail() throws MessagingException {

        String from = environment.getProperty("spring.mail.from");
        String to = environment.getProperty("spring.mail.to");

        // 使用Mime消息体
        MimeMessage message = mailSender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message, true);

        helper.setFrom(from);
        helper.setTo(to);

        helper.setSubject("first html report from yourself");

        // 根据模板、变量生成内容

        // 数据模型
        List<Pet> pets = new ArrayList<Pet>();
        pets.add(new Pet("Polly", "Bird", 2));
        pets.add(new Pet("Tom", "Cat", 5));
        pets.add(new Pet("Badboy", "Dog", 3));
        
        Context context = new Context();
        context.setVariable("customer", "LiLei");
        context.setVariable("pets", pets);

        String text = templateEngine.process("mail/template", context);
        helper.setText(text, true);

        mailSender.send(message);
    }

}

启动应用,发送邮件后的效果:

三、CID与图片

使用 thymeleaf 可以快速的制作出一个Html模板,
有时候我们需要在邮件中显示一张图片,怎么办呢?

  1. 使用img标签,并指定一个在线的图片;
    此方案比较通用,应该说大多数在线平台都采用这种做法,但这么做的前提是需要有一个统一的图片存储及访问系统。
  2. 使用 Base64编码,在页面中嵌入编码后的内容:
<img width="100" height="100" src="data:image/jpg;base64,/9dxxFEF8fEkqAAgAAAAL===" />

该方案非通用,在实测中发现Outlook 无法展示这类标签,客户端并未支持。
下面列举了支持内嵌图片展示的一些邮件客户端:

  1. 采用CID 方案,图片作为内嵌资源

CID就是ContentID,是一种在MIME消息体中用于定义并引用内容块的机制。
RFC2392 对这个进行了定义。

一个带CID的消息体如下所示:

--boundary-example 1
Content-Type: Text/HTML; charset=US-ASCII

to the other body part, for example through a statement such as:
<IMG SRC="cid:foo4*foo1@bar.net" ALT="IETF logo">

--boundary-example-1

Content-ID: <foo4*foo1@bar.net>
Content-Type: IMAGE/GIF
Content-Transfer-Encoding: BASE64

R0lGODlhGAGgAPEAAP/////ZRaCgoAAAACH+PUNvcHlyaWdodCAoQykgMTk5
NSBJRVRGLiBVbmF1dGhvcml6ZWQgZHVwbGljYXRpb24gcHJvaGliaXRlZC4A
etc...

那么,使用CID内嵌图片的做法如下:

步骤一

在发送邮件时指定带 CID 的 Resource

        String text = templateEngine.process("mail/template", context);
        helper.setText(text, true);

        helper.addInline("soft", new FileSystemResource("D:/temp/soft.png"));
        mailSender.send(message);

步骤二
步骤:模板中引用对应的CID,如下:

<img src="cid:soft"></img>

最终,发送邮件可支持图片的展示,如下

参考文档

spring.io-mail
springboot-mail.properties
send-a-base64-image-in-html-email

欢迎继续关注"美码师的补习系列-springboot篇" ,期待更多精彩内容^-^

原文链接:https://www.cnblogs.com/littleatp/p/9973261.html

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Spring全家桶--SpringBoot(二)
Spring全家桶--SpringBoot(二)
120 0
Spring全家桶--SpringBoot(一)
Spring全家桶--SpringBoot(一)
69 0
Spring全家桶--SpringBoot(三)
Spring全家桶--SpringBoot(三)
26 0
Spring全家桶系列--SpringBoot入门Redis
Spring全家桶系列--SpringBoot入门Redis
39 0
补习系列(18)-springboot H2 迷你数据库
关于 H2 H2 数据库是一个纯 Java 实现的开源的嵌入式数据库,笔者第一次使用时还是在多年前的一个客户端项目中。当时就觉得这个数据库很方便,如果你希望你的应用程序能"自带数据库,随处运行”,那么H2是个不错的选择。
4990 0
补习系列(17)-springboot mongodb 内嵌数据库
简介 前面的文章中,我们介绍了如何在SpringBoot 中使用MongoDB的一些常用技巧。那么,与使用其他数据库如 MySQL 一样,我们应该怎么来做MongoDB的单元测试呢? 使用内嵌数据库的好处是不需要依赖于一个外部环境,如果每一次跑单元测试都需要依赖一个稳定的外部环境,那么这样的测试是极不稳定的。
1673 0
补习系列(15)-springboot 分布式会话原理
[TOC] 一、背景 在 补习系列(3)-springboot 几种scope 一文中,笔者介绍过 Session的部分,如下: 对于服务器而言,Session 通常是存储在本地的,比如Tomcat 默认将Session 存储在内存(ConcurrentHashMap)中。
2373 0
补习系列(6)-SpringBoot 整合Shiro 一指禅
SpringBoot 整合Shiro,基于starter自动装配,老司机提供的最完整实例
4867 0
补习系列(4)-springboot 参数校验详解
对于几种常见的入参方式,了解如何进行校验以及该如何处理错误消息; 了解springboot 内置的参数异常类型,并能利用拦截器实现自定义处理; 能实现简单的自定义校验规则
3494 0
springboot2.x中的服务监控
想给服务添加一个监控,看看网上各位前辈的,基本都是基于springboot1.x的,springboot升级到2.0以后和1.x还是有很多不一样的,那么2.0以后怎么使用admin监控呢? 先看下图的managment.security.enable,现在已经是过时API了,那么我们必须要更新知识库了。
1346 0
+关注
美码师
美码师,老码农一枚,唯美食与技术不能辜负,欢迎交流及打扰!
73
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载