• 补习系列(12)-springboot 与邮件发送【华为云技术分享】


    目录

    一、邮件协议

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

    电子邮件协议是基于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 //收发人
    11 spring.mail.from=xxx@qq.com
    12 spring.mail.to=xxx@qq.com
    13 
    14 //启用鉴权
    15 spring.mail.properties.mail.smtp.auth=true
    16 //不使用tls
    17 spring.mail.properties.mail.smtp.starttls.enable=false
    18 spring.mail.properties.mail.smtp.starttls.required=false

    C. 发送文本邮件

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

     1 @Service
     2 public class SimpleMailSender implements CommandLineRunner {
     3 
     4     private static final Logger logger = LoggerFactory.getLogger(SimpleMailSender.class);
     5 
     6     @Autowired
     7     private JavaMailSender mailSender;
     8 
     9     @Autowired
    10     private Environment environment;
    11 
    12     private void sendText() {
    13         String from = environment.getProperty("spring.mail.from");
    14         String to = environment.getProperty("spring.mail.to");
    15 
    16         SimpleMailMessage msg = new SimpleMailMessage();
    17         msg.setFrom(from);
    18         msg.setTo(to);
    19 
    20         msg.setSubject("first email from yourself");
    21         msg.setText("hello world!");
    22 
    23         this.mailSender.send(msg);
    24         logger.info("send text done");
    25     }
    26 
    27     @Override
    28     public void run(String... args) throws Exception {
    29         sendText();
    30     }

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

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

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

    D.发送附件

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

     1   
     2     private void sendAttachment() throws MessagingException {
     3         String from = environment.getProperty("spring.mail.from");
     4         String to = environment.getProperty("spring.mail.to");
     5 
     6         // 使用Mime消息体
     7         MimeMessage message = mailSender.createMimeMessage();
     8 
     9         // multipart参数为true,表示需要发送附件
    10         MimeMessageHelper helper = new MimeMessageHelper(message, true);
    11         helper.setFrom(from);
    12         helper.setTo(to);
    13 
    14         helper.setSubject("first file from yourself");
    15         helper.setText("check the file");
    16 
    17         //指定系统文件
    18         File file = new File("D:\temp\attachment.xlsx");
    19         FileSystemResource resource = new FileSystemResource(file);
    20         helper.addAttachment(file.getName(), resource);
    21 
    22         mailSender.send(message);
    23 
    24         logger.info("send attachment done");
    25     }

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

    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 
     4 <h4 th:text="|Hi, ${customer}, these're your pets|"></h4>
     5 <hr></hr>
     6 
     7 <table>
     8   <tr>
     9     <th>name</th>
    10     <th>type</th>
    11     <th>age</th>
    12   </tr>
    13   <tr th:each="p : ${pets}">
    14     <td th:text="${p.name}"></td>
    15     <td th:text="${p.type}"></td>
    16     <td th:text="${p.age}"></td>
    17   </tr>
    18 </table>
    19 
    20 </body>
    21 </html>

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

     1 public static class Pet {
     2 
     3     private String name;
     4     private String type;
     5     private int age;
     6 
     7     public Pet(String name, String type, int age) {
     8         super();
     9         this.name = name;
    10         this.type = type;
    11         this.age = age;
    12     }
    13 ...

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

     1 @Service
     2 public class SimpleMailSender {
     3     /**
     4      * 日志工具
     5      */
     6     private static final Logger logger = LoggerFactory.getLogger(MailService.class);
     7 
     8     @Autowired
     9     private JavaMailSender mailSender;
    10 
    11     @Autowired
    12     private TemplateEngine templateEngine;
    13 
    14     @Autowired
    15     private Environment environment;
    16 
    17     private void sendTemplateMail() throws MessagingException {
    18 
    19         String from = environment.getProperty("spring.mail.from");
    20         String to = environment.getProperty("spring.mail.to");
    21 
    22         // 使用Mime消息体
    23         MimeMessage message = mailSender.createMimeMessage();
    24 
    25         MimeMessageHelper helper = new MimeMessageHelper(message, true);
    26 
    27         helper.setFrom(from);
    28         helper.setTo(to);
    29 
    30         helper.setSubject("first html report from yourself");
    31 
    32         // 根据模板、变量生成内容
    33 
    34         // 数据模型
    35         List<Pet> pets = new ArrayList<Pet>();
    36         pets.add(new Pet("Polly", "Bird", 2));
    37         pets.add(new Pet("Tom", "Cat", 5));
    38         pets.add(new Pet("Badboy", "Dog", 3));
    39         
    40         Context context = new Context();
    41         context.setVariable("customer", "LiLei");
    42         context.setVariable("pets", pets);
    43 
    44         String text = templateEngine.process("mail/template", context);
    45         helper.setText(text, true);
    46 
    47         mailSender.send(message);
    48     }
    49 
    50 }

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

    三、CID与图片

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

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

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

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

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

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

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

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

     1 --boundary-example 1
     2 Content-Type: Text/HTML; charset=US-ASCII
     3 
     4 to the other body part, for example through a statement such as:
     5 <IMG SRC="cid:foo4*foo1@bar.net" ALT="IETF logo">
     6 
     7 --boundary-example-1
     8 
     9 Content-ID: <foo4*foo1@bar.net>
    10 Content-Type: IMAGE/GIF
    11 Content-Transfer-Encoding: BASE64
    12 
    13 R0lGODlhGAGgAPEAAP/////ZRaCgoAAAACH+PUNvcHlyaWdodCAoQykgMTk5
    14 NSBJRVRGLiBVbmF1dGhvcml6ZWQgZHVwbGljYXRpb24gcHJvaGliaXRlZC4A
    15 etc...

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

    步骤一

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

    1         String text = templateEngine.process("mail/template", context);
    2         helper.setText(text, true);
    3 
    4         helper.addInline("soft", new FileSystemResource("D:/temp/soft.png"));
    5         mailSender.send(message);

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

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

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

    码云同步代码

    参考文档

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

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

    作者:美码师

    HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。

    欢迎报名参会

  • 相关阅读:
    那些H5用到的技术(4)——弹幕
    Android自动化之AccessibilityService
    那些H5用到的技术(3)——屏幕场景滑动
    更新Mac双系统多分区
    那些H5用到的技术(2)——音频和视频播放
    那些H5用到的技术(1)——素材加载
    openstf安装手记
    我的2016年总结
    与为知笔记融合-博客园装修笔记
    点击劫持(click jacking)
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/12018150.html
Copyright © 2020-2023  润新知