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

我怕爱的太早我们不能终老 提交于 2020-12-18 03:03:42

一、邮件协议

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

电子邮件协议是基于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是一个封装了邮件功能的组件,依赖如下:

  
    
  
  
  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-mail</artifactId>

  4. <version>${spring-boot.version}</version>

  5. </dependency>


B. 配置文件

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

  
    
  
  
  1. //smtp 服务器

  2. spring.mail.host=smtp.qq.com

  3. //smtp 端口

  4. spring.mail.port=25

  5. //发送用户名

  6. spring.mail.username=xxx

  7. //发送密码

  8. spring.mail.password=xxx

  9. //收发人

  10. spring.mail.from=xxx@qq.com

  11. spring.mail.to=xxx@qq.com

  12. //启用鉴权

  13. spring.mail.properties.mail.smtp.auth=true

  14. //不使用tls

  15. spring.mail.properties.mail.smtp.starttls.enable=false

  16. spring.mail.properties.mail.smtp.starttls.required=false


C. 发送文本邮件

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

  
    
  
  
  1. @Service

  2. public class SimpleMailSender implements CommandLineRunner {

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

  4.    @Autowired

  5.    private JavaMailSender mailSender;

  6.    @Autowired

  7.    private Environment environment;

  8.    private void sendText() {

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

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

  11.        SimpleMailMessage msg = new SimpleMailMessage();

  12.        msg.setFrom(from);

  13.        msg.setTo(to);

  14.        msg.setSubject("first email from yourself");

  15.        msg.setText("hello world!");

  16.        this.mailSender.send(msg);

  17.        logger.info("send text done");

  18.    }

  19.    @Override

  20.    public void run(String... args) throws Exception {

  21.        sendText();

  22.    }

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

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

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


D.发送附件

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

  
    
  
  
  1.    private void sendAttachment() throws MessagingException {

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

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

  4.        // 使用Mime消息体

  5.        MimeMessage message = mailSender.createMimeMessage();

  6.        // multipart参数为true,表示需要发送附件

  7.        MimeMessageHelper helper = new MimeMessageHelper(message, true);

  8.        helper.setFrom(from);

  9.        helper.setTo(to);

  10.        helper.setSubject("first file from yourself");

  11.        helper.setText("check the file");

  12.        //指定系统文件

  13.        File file = new File("D:\\temp\\attachment.xlsx");

  14.        FileSystemResource resource = new FileSystemResource(file);

  15.        helper.addAttachment(file.getName(), resource);

  16.        mailSender.send(message);

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

  18.    }


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


E. 发送Html邮件

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

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

  
    
  
  
  1. <dependency>

  2. <groupId>org.springframework.boot</groupId>

  3. <artifactId>spring-boot-starter-thymeleaf</artifactId>

  4. <version>${spring-boot.version}</version>

  5. </dependency>


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

  
    
  
  
  1. <html>

  2. <body>

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

  4. <hr></hr>

  5. <table>

  6.  <tr>

  7.    <th>name</th>

  8.    <th>type</th>

  9.    <th>age</th>

  10.  </tr>

  11.  <tr th:each="p : ${pets}">

  12.    <td th:text="${p.name}"></td>

  13.    <td th:text="${p.type}"></td>

  14.    <td th:text="${p.age}"></td>

  15.  </tr>

  16. </table>

  17. </body>

  18. </html>


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

  
    
  
  
  1. public static class Pet {

  2.    private String name;

  3.    private String type;

  4.    private int age;

  5.    public Pet(String name, String type, int age) {

  6.        super();

  7.        this.name = name;

  8.        this.type = type;

  9.        this.age = age;

  10.    }

  11. ...


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

  
    
  
  
  1. @Service

  2. public class SimpleMailSender {

  3.    /**

  4.     * 日志工具

  5.     */

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

  7.    @Autowired

  8.    private JavaMailSender mailSender;

  9.    @Autowired

  10.    private TemplateEngine templateEngine;

  11.    @Autowired

  12.    private Environment environment;

  13.    private void sendTemplateMail() throws MessagingException {

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

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

  16.        // 使用Mime消息体

  17.        MimeMessage message = mailSender.createMimeMessage();

  18.        MimeMessageHelper helper = new MimeMessageHelper(message, true);

  19.        helper.setFrom(from);

  20.        helper.setTo(to);

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

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

  23.        // 数据模型

  24.        List<Pet> pets = new ArrayList<Pet>();

  25.        pets.add(new Pet("Polly", "Bird", 2));

  26.        pets.add(new Pet("Tom", "Cat", 5));

  27.        pets.add(new Pet("Badboy", "Dog", 3));

  28.        Context context = new Context();

  29.        context.setVariable("customer", "LiLei");

  30.        context.setVariable("pets", pets);

  31.        String text = templateEngine.process("mail/template", context);

  32.        helper.setText(text, true);

  33.        mailSender.send(message);

  34.    }

  35. }


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


三、CID与图片

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

1.  使用img标签,并指定一个在线的图片; 此方案比较通用,应该说大多数在线平台都采用这种做法,但这么做的前提是需要有一个统一的图片存储及访问系统。

2.  使用 Base64编码,在页面中嵌入编码后的内容:

  
    
  
  
  1. <img width="100" height="100" src="data:image/jpg;base64,

  2.            /9dxxFEF8fEkqAAgAAAAL===" />

该方案非通用,在实测中发现Outlook 无法展示这类标签,客户端并未支持。 


下面列举了支持内嵌图片展示的一些邮件客户端:

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

CID就是ContentID,是一种在MIME消息体中用于定义并引用内容块的机制。 

RFC2392 对这个进行了定义。

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

  
    
  
  
  1. --boundary-example 1

  2. Content-Type: Text/HTML; charset=US-ASCII

  3. to the other body part, for example through a statement such as:

  4. <IMG SRC="cid:foo4*foo1@bar.net" ALT="IETF logo">

  5. --boundary-example-1

  6. Content-ID: <foo4*foo1@bar.net>

  7. Content-Type: IMAGE/GIF

  8. Content-Transfer-Encoding: BASE64

  9. R0lGODlhGAGgAPEAAP/////ZRaCgoAAAACH+PUNvcHlyaWdodCAoQykgMTk5

  10. NSBJRVRGLiBVbmF1dGhvcml6ZWQgZHVwbGljYXRpb24gcHJvaGliaXRlZC4A

  11. etc...


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

步骤一

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

  
    
  
  
  1.        String text = templateEngine.process("mail/template", context);

  2.        helper.setText(text, true);

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

  4.        mailSender.send(message);

步骤二 

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

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


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


参考文档

spring.io-mail 

springboot-mail.properties 

send-a-base64-image-in-html-email

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




相关文章推荐:

补习系列(11)-springboot 文件上传原理

补习系列(10)-springboot 之配置读取

补习系列(9)- springboot 定时器,你用对了吗

补习系列(8)-springboot 单元测试之道

补习系列(7)-springboot 拦截器五大姿势

补习系列(6)- springboot 整合 shiro 一指禅

补习系列(5)-springboot restful实战


本文分享自微信公众号 - 美码师(gracebuilding)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!