补习系列(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文档 | 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模板,
有时候我们需要在邮件中显示一张图片,怎么办呢?
使用img标签,并指定一个在线的图片;
此方案比较通用,应该说大多数在线平台都采用这种做法,但这么做的前提是需要有一个统一的图片存储及访问系统。使用 Base64编码,在页面中嵌入编码后的内容:
<img width="100" height="100" src="data:image/jpg;base64,/9dxxFEF8fEkqAAgAAAAL===" />
该方案非通用,在实测中发现Outlook 无法展示这类标签,客户端并未支持。
下面列举了支持内嵌图片展示的一些邮件客户端:
- 采用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篇" ,期待更多精彩内容-
补习系列(12)-springboot 与邮件发送的更多相关文章
- 补习系列(12)-springboot 与邮件发送【华为云技术分享】
目录 一.邮件协议 关于数据传输 二.SpringBoot 与邮件 A. 添加依赖 B. 配置文件 C. 发送文本邮件 D.发送附件 E. 发送Html邮件 三.CID与图片 参考文档 一.邮件协议 ...
- springboot添加邮件发送及压缩功能
springboot添加邮件发送及文件压缩功能 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9190233.html 先来一段诗 ``` 就这样吧 忍受折磨 ...
- SpringBoot集成邮件发送
一:简述 在日常中的工作中难免会遇到程序集成邮件发送功能.接收功能:此篇文章我将使用SpringBoot集成邮件发送功能和接收功能:若对邮件一些基本协议和发送流程不懂的请务必参考我之前写的博客或者浏览 ...
- 补习系列(15)-springboot 分布式会话原理
目录 一.背景 二.SpringBoot 分布式会话 三.样例程序 四.原理进阶 A. 序列化 B. 会话代理 C. 数据老化 小结 一.背景 在 补习系列(3)-springboot 几种scope ...
- 补习系列(14)-springboot redis 整合-数据读写
目录 一.简介 二.SpringBoot Redis 读写 A. 引入 spring-data-redis B. 序列化 C. 读写样例 三.方法级缓存 四.连接池 小结 一.简介 在 补习系列(A3 ...
- 补习系列(2)-springboot mime类型处理
目标 了解http常见的mime类型定义: 如何使用springboot 处理json请求及响应: 如何使用springboot 处理 xml请求及响应: http参数的获取及文件上传下载: 如何获得 ...
- 补习系列(13)-springboot redis 与发布订阅
目录 一.订阅发布 常见应用 二.Redis 与订阅发布 三.SpringBoot 与订阅发布 A. 消息模型 B. 序列化 C. 发布消息 D. 接收消息 小结 一.订阅发布 订阅发布是一种常见的设 ...
- 补习系列(10)-springboot 之配置读取
目录 简介 一.配置样例 二.如何注入配置 1. 缺省配置文件 2. 使用注解 3. 启动参数 还有.. 三.如何读取配置 @Value 注解 Environment 接口 @Configuratio ...
- 补习系列(9)-springboot 定时器,你用对了吗
目录 简介 一.应用启动任务 二.JDK 自带调度线程池 三.@Scheduled 定制 @Scheduled 线程池 四.@Async 定制 @Async 线程池 小结 简介 大多数的应用程序都离不 ...
随机推荐
- bzoj 2500 幸福的道路 树上直径+set
首先明确:树上任意一点的最长路径一定是直径的某一端点. 所以先找出直径,求出最长路径,然后再求波动值<=m的最长区间 #include<cstdio> #include<cst ...
- BZOJ_3083_遥远的国度_树链剖分+线段树
BZOJ_3083_遥远的国度_树链剖分 Description 描述 zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神Ra ...
- WAMP下配置FCGID+ZendGuardLoader
公司的项目里,有几个文件是被加密的,经过一翻折腾,终于配置成功 文件加密技术用的是ZendGuard,所以必须安装的PHP必须得是nts的 一.下载并配置PHP 先下载安装php,注意VC版本和是否n ...
- WebService-----Xfire
Xfire相对Axis2 个人感觉相对简单一点,因为myEclipse上可以自动生成.而且在目前来讲,Xfire也是更受欢迎一点. 首先说明Xfire所用jar包:http://download.cs ...
- Android 7.0 启动篇 — init原理(一)(转 Android 9.0 分析)
======================================================== ================================== ...
- 为自己搭建一个分布式 IM(即时通讯) 系统
前言 大家新年快乐! 新的一年第一篇技术文章希望开个好头,所以元旦三天我也没怎么闲着,希望给大家带来一篇比较感兴趣的干货内容. 老读者应该还记得我在去年国庆节前分享过一篇<设计一个百万级的消息推 ...
- entity cannot be tracked
背景:EF Core项目中使用InMemory作为数据库提供程序,编写单元测试. 报错:“The instance of entity type 'Movie' cannot be tracked b ...
- BootStrap:轮播插件
前述 利用 BootStrap 实现图片轮播,包括 基本轮播.带标题的轮播.设置轮播速度的轮播.控制前后的轮播 实例 基本轮播 代码 1.引入bootstrap和jQuery文件 <!-- jQ ...
- 深度辨析 Python 的 eval() 与 exec()
Python 提供了很多内置的工具函数(Built-in Functions),在最新的 Python 3 官方文档中,它列出了 69 个. 大部分函数是我们经常使用的,例如 print().open ...
- Linux三剑客-grep || awk || sed
grep是一个强大的文本搜索工具 命令格式: grep [option] pattren file -a 将二进制文档以文本方式处理 -c 计算找到的符合行的次数 -i 忽略大小写 -n 顺便 ...