沉浸式求职学习
邮件发送原理及实现
1.概述
传输协议
- SMTP协议
发送邮件:
我们通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。 - POP3协议
接收邮件:
我们通常把处理用户pop3请求(邮件接收请求)的服务器称之为POP3服务器(邮件接收服务器)。
邮件收发原理
- 请参考:连接
使用Java实现邮件发送需要使用到的类
-
我们将用代码完成邮件的发送。这在实际项目中应用的非常广泛,比如注册需要发送邮件进行账号激活,再比如A项目中利用邮件进行任务提醒等等。
-
使用Java发送Emai分简单,但是首先你应该准备 JavaMail API和 Java Activation framework。
-
得到两个jar包:
-
JavaMail是sωn公司(现以被甲骨文收购)为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,它支持一些常用的邮件协议,如前面所讲的SMTP,POP3,IMAP,还有MIME等。我们在使用 JavaMail!API编写邮件时,无须考虑邮件的底层实现细节,只要调用 Javamail开发包中相应的API类就可以了。
-
我们可以先尝试发送一封简单的邮件,确保电脑可以连接网络。
- 创建包含邮件服务器的网络连接信息的Session对象。
- 创建代表邮件内容的Message对象;
- 创建Transport对象,连接服务器,发送Message,关闭连接;
-
主要有四个核心类,我们在编写程序时,记住这四个核心类,就很容易编写出Java邮件处理程序。
2.简单邮件
邮件分类
- 简单邮件:没有除了文字以外的其他所有文件(包括附件和图片、视频等),即纯文本邮件;
- 复杂邮件:除了传统的文字信息外,还包括了一些非文字数据的邮件;
需要发送邮件首先就要我们的邮箱账号支持POP3和SMTP协议,所以我们需要开启邮箱的POP3+SMTP服务,然后我们需要复制下图中的授权码,这个授权码就相当于你的QQ密码,你可以使用你的邮箱账号+授权码来发送邮件,而SMTP服务器也就是使用这个来识别你的身份的。

java
package com.github.test;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/**
* @Author: subei
* @Description: 一封简单的邮件
*/
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com");
// 设置QQ邮件服务器
prop.setProperty("mail.transport.protocol", "smtp");
// 邮件发送协议
prop.setProperty("mail.smtp.auth", "true");
// 需要验证用户名密码
// 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// 使用JavaMail发送邮件的5个步骤
// 1.创建定义整个应用程序所需的环境信息的 Session 对象
// 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
Session session = Session.getDefaultInstance(prop, new Authenticator() {
// 获取和SMTP服务器的连接对象
@Override
public PasswordAuthentication getPasswordAuthentication() {
// 发件人邮件用户名、授权码
return new PasswordAuthentication("[email protected]", "授权码");
}
});
// 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
// 2.通过session得到transport对象
Transport ts = session.getTransport();
// 通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象
// 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
ts.connect("smtp.qq.com", "[email protected]", "授权码");
// 4.创建邮件对象MimeMessage------点击网页上的写信
// 创建一个邮件对象
MimeMessage message = new MimeMessage(session);
// 指明邮件的发件人------在网页上填写发件人
message.setFrom(new InternetAddress("[email protected]"));
// 设置发件人
// 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发------在网页上填写收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));
// 设置收件人
// 邮件的标题------在网页上填写邮件标题
message.setSubject("简单邮件发送实现");
// 设置邮件主题
// 邮件的文本内容------在网页上填写邮件内容
message.setContent("<h2 style='color:red'>你好啊!</h2>", "text/html;charset=UTF-8");
// 设置邮件的具体内容
// 5.发送邮件------在网页上点击发送按钮
ts.sendMessage(message, message.getAllRecipients());
// 6.关闭连接对象,即关闭服务器上的连接资源
ts.close();
}
}
运行测试:
3.复杂邮件
-
复杂邮件就是非纯文本的邮件,可能还包含了图片和附件等资源。
-
MIME(多用途互联网邮件扩展类型)
-
先认识两个类一个名词:
-
MimeBodyPart类
- javax.mail.internet.MimeBodyPart类表示的是一个MIME消息,它和MimeMessage类一样都是从Part接口继承过来。
- 即一个MIME消息对应一个MimeBodyPart对象,而MimeBodyPart对象就是我们写的邮件内容中的元素。
-
MimeMultipart类
- javax.mail.internet.MimeMultipart是抽象类 Multipart的实现子类,它用来组合多个MIME消息。一个MimeMultipart对象可以包含多个代表MIME消息的MimeBodyPart对象。
- 即一个MimeMultipart对象表示多个MimeBodyPart的集合,而一个MimeMultipart表示的就是我们一封邮件的内容。
-
MimeMultipart对象的使用的时候需要设置setSubType()的属性值,一共就下面3种取值:
- alternative:表明这个MimeMultipart对象中的MimeMessage对象的数据是纯文本文件;
- related:表明这个MimeMultipart对象中的MimeMessage对象的数据包含非纯文本文件;
- mixed:表明这个MimeMultipart对象中的MimeMessage对象的数据包含附件;
我们在使得的时候如果不知道使用哪一个,直接使用mixed即可,使用这个属性值一定不会报错。

- 相较于简单邮件,复杂邮件变化的地方只是在于邮件内容本身会发送变化,而其他的步骤都是一样的:
- 准备一些参数;
- 获取session对象;
- 获取传输对象;
- 登陆授权;
- 写邮件 (和简单邮件相区别);
- 发送邮件;
- 关闭服务器资源。
发送包含图片的复杂邮件
java
package com.github.test;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.util.Properties;
/**
* @Author: subei
* @Description: 图片的邮件
*/
public class MailDemo01 {
public static void main(String[] args) throws Exception {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com");
// 设置QQ邮件服务器
prop.setProperty("mail.transport.protocol", "smtp");
// 邮件发送协议
prop.setProperty("mail.smtp.auth", "true");
// 需要验证用户名密码
// 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// 使用JavaMail发送邮件的5个步骤
// 1.创建定义整个应用程序所需的环境信息的 Session 对象
// 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
Session session = Session.getDefaultInstance(prop, new Authenticator() {
// 获取和SMTP服务器的连接对象
@Override
public PasswordAuthentication getPasswordAuthentication() {
// 发件人邮件用户名、授权码
return new PasswordAuthentication("[email protected]", "授权码");
}
});
// 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
// 2.通过session得到transport对象
Transport ts = session.getTransport();
// 通过这一次和SMTP服务器的连接对象获取发送邮件的传输对象
// 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
ts.connect("smtp.qq.com", "[email protected]", "授权码");
// 4.创建邮件对象MimeMessage------点击网页上的写信
// 创建一个邮件对象
MimeMessage message = new MimeMessage(session);
// 指明邮件的发件人------在网页上填写发件人
message.setFrom(new InternetAddress("[email protected]"));
// 设置发件人
// 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发------在网页上填写收件人
message.setRecipient(Message.RecipientType.TO, new InternetAddress("[email protected]"));
// 设置收件人
// 邮件的标题------在网页上填写邮件标题
message.setSubject("简单邮件发送实现");
// 设置邮件主题
System.out.println("=============================复杂邮件的邮件内容设置==================================");
// 准备邮件数据
// 准备图片数据
// 获取一个图片的MimeBodyPart对象
MimeBodyPart image = new MimeBodyPart();
// 由于图片需要字符化才能传输,所以需要获取一个DataHandler对象
DataHandler dh = new DataHandler(new FileDataSource("图片的绝对地址"));
// 将图片序列化
image.setDataHandler(dh);
// 为图片的MimeBodyPart对象设置一个ID,我们在文字中就可以使用它了
image.setContentID("p6.jpg");
// 准备正文数据
MimeBodyPart text = new MimeBodyPart();
// 获取一个文本的MimeBodyPart对象
text.setContent("这是一封邮件正文带图片<img src='cid:p6.jpg'>的邮件", "text/html;charset=UTF-8");
// 设置文本内容,注意在里面嵌入了<img src='cid:p6.jpg'>
// 描述数据关系
// 获取MimeMultipart
MimeMultipart mm = new MimeMultipart();
// 将文本MimeBodyPart对象加入MimeMultipart中
mm.addBodyPart(text);
// 将图片MimeBodyPart对象加入MimeMultipart中
mm.addBodyPart(image);
// 设置MimeMultipart对象的相对熟悉为related,即发送的数据为文本+非附件资源
mm.setSubType("related");
// 设置到消息中,保存修改
message.setContent(mm);
// 将MimeMultipart放入消息对象中
message.saveChanges();
// 保存上面的修改
System.out.println("===============================================================");
// 5.发送邮件------在网页上点击发送按钮
ts.sendMessage(message, message.getAllRecipients());
// 6.关闭连接对象,即关闭服务器上的连接资源
ts.close();
}
}
测试运行:
网站注册发送邮件功能实现
java
package com.github.pojo;
public class User {
private String username;
private String password;
private String email;
public User() {
}
public User(String username, String password, String email) {
this.username = username;
this.password = password;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", email='" + email + '\'' +
'}';
}
}
编写工具类Sendmail.java:
java
package com.github.util;
/**
* 多线程实现邮件发送
* 使用多线程的原因就是提高用户的体验,一旦一个页面3s及以上的时间白屏就可能被用户关掉
* 所以我们在用户提交表单之后,将费时的邮件发送工作使用一个子线程来完成,而主线程还是去完成它自己的事情
* 这么做就可以提高用户体验,不然用户等待邮件发送的时间
*/
import com.github.pojo.User;
import com.sun.mail.util.MailSSLSocketFactory;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
/**
* @author subei
* 多线程这种处理就可以称为异步处理
*/
public class Sendmail extends Thread{
// 用于向客户发送邮件的邮箱
private String from = "[email protected]";
// 用于登陆SMTP服务器的用户名
private String username = "[email protected]";
// 授权码
private String password = "授权码";
// 发送邮件的地址
private String host = "smtp.qq.com";
private User user;
public Sendmail(User user) {
this.user = user;
}
@Override
public void run() {
try {
Properties prop = new Properties();
// 设置QQ邮件服务器
prop.setProperty("mail.host", host);
// 邮件发送协议
prop.setProperty("mail.transport.protocol", "smtp");
// 需要验证用户名密码
prop.setProperty("mail.smtp.auth", "true");
// 关于QQ邮箱,还要设置SSL加密,加上以下代码即可
MailSSLSocketFactory sf = new MailSSLSocketFactory();
sf.setTrustAllHosts(true);
prop.put("mail.smtp.ssl.enable", "true");
prop.put("mail.smtp.ssl.socketFactory", sf);
// 1.创建定义整个应用程序所需的环境信息的 Session 对象
// 使用QQ邮箱的时候才需要,其他邮箱不需要这一段代码
// 获取和SMTP服务器的连接对象
Session session = Session.getDefaultInstance(prop, new Authenticator() {
@Override
public PasswordAuthentication getPasswordAuthentication() {
// 发件人邮件用户名、授权码
return new PasswordAuthentication("[email protected]", "授权码");
}
});
// 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
session.setDebug(true);
// 2.通过session得到transport对象
Transport ts = session.getTransport();
// 3.使用邮箱的用户名和授权码连上SMTP邮件服务器,即登陆
ts.connect(host, username, password);
//4、创建邮件对象MimeMessage------点击网页上的写信
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(username));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
message.setSubject("用户注册邮件!");
message.setContent("<p><h2>恭喜注册成功!</h2></p>您的用户名为: <h4>"+user.getUsername()+
"</h4><p>您的密码:" + user.getPassword() +
"</p><p>请妥善保管您的密码,如有问题请及时联系网站客服,再次欢迎您的加入!!</p>", "text/html;charset=UTF-8");
// 5.发送邮件------在网页上点击发送按钮
ts.sendMessage(message, message.getAllRecipients());
// 6.关闭连接对象,即关闭服务器上的连接资源
ts.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
编写Servlet.java:
java
package com.github.servlet;
import com.github.pojo.User;
import com.github.util.Sendmail;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class RegisterServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.接收用户填写的表单数据
String username = req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
System.out.println(username+password+email);
// 2.向用户邮箱发送邮件,注意发送邮件很耗时,所以我们启动一个子线程去做这件事,而用户则是直接跳转注册成功页面,以免降低用户体验
User user = new User(username,password,email);
// 获取子线程对象
Sendmail sendmail = new Sendmail(user);
// 启动子线程
new Thread(sendmail).start();
// 3.视图跳转
req.setAttribute("message","注册成功!我们已经向您的邮箱发送了邮件,请您及时进行查收。由于网络原因,您收到邮件的时间存在延迟,敬请谅解~");
req.getRequestDispatcher("info.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}

🎉🎉全剧终🎉🎉