SpringBoot實現發送電子郵件

SpringBoot知識體系

目錄


從1969年10月世界上的第一封電子郵件發出,到2019年,已經過去將近半個世紀了。雖然即時通訊和視頻會議,甚至全息投影都變得日益普及,但電子郵件依然有着廣泛的使用場景和不可撼動的歷史地位。

SpringBoot擁有強大的生態鏈,幾乎可以連接所有主流的開源庫。

下面我們就從電子郵件發送的歷史再到原理,然後如何自己配置郵件服務器併發送郵件,一步步講解。

本文實現源碼可以在這裏找到: SpringBoot發送電子郵件源碼

電子郵件與Java發送郵件的歷史

  1. 1969年10月,世界上的第一封電子郵件

1969年10月世界上的第一封電子郵件是由計算機科學家Leonard K.教授發給他的同事的一條簡短消息。第一條網上信息就是‘LO’,意思是‘你好!’。

  1. 1987年9月14日中國的第一封電子郵件

在此之後,1987年9月14日中國的第一封電子郵件,這封郵件是由德國維爾納·措恩與中國的王運豐在北京計算機應用技術研究所,發往德國一個大學的,郵件內容頗具深意,“Across the Great Wall we can reach every corner in the world.(越過長城,走向世界)”,這是中國通過北京與德國大學之間的網絡連接,向全球科學網發出的第一封電子郵件。

  1. 30年代發展歷程

接下來中國的電子郵件進入了30年的發展期,雖然在1987年就有了電子郵件,但是,真正的郵件興起,應該在90年代到2000年之間,因爲在1987的時候中國網速特別慢,真正能接觸到互聯網的用戶是非常少的,到了90年代中期,互聯網瀏覽器的誕生,使得全民上網人數激增,電子郵件被廣泛使用,此時,中國的部分學生在研究中使用到電子郵件,真正普及的時間是在2000年左右。

  1. Java發送郵件

Java在發明之初,就開始支持發送郵件,通過java mail包方式去操作郵件發送的內容和協議,但是,這種發送方式稍微比較複雜,需要配置各種參數、協議、內容,之後產生了Spring框架。

  1. Spring發送郵件

Spring在java mail的基礎上進行了一些封裝,使發送郵件的過程的複雜大大減少。

  1. SpringBoot發送郵件

SpringBoot Mail在Spring Mail的基礎上,再次進行一次封裝,使得發送郵件的便利度上,更爲簡單。

電子郵件原理

電子郵件服務器

用戶要在Internet上提供電子郵件功能,必須有專門的電子郵件服務器。這些郵件服務器就類似於現實生活中的郵局,它主要負責接收用戶投遞過來的郵件,並把郵件投遞到郵件接收者的電子郵箱中。

郵件服務器就好像是互聯網世界的郵局。按照功能劃分,郵件服務器可以劃分爲兩種類型:

  1. SMTP郵件服務器:用戶替用戶發送郵件和接收外面發送給本地用戶的郵件。
  2. POP3/IMAP郵件服務器:用戶幫助用戶讀取SMTP郵件服務器接收進來的郵件。

電子郵箱

電子郵箱也稱爲E-mail地址,用戶可以通過E-mail地址來標識自己發送的電子郵件,也可以通過這個地址接收別人發來的電子郵件。電子郵箱需要到郵件服務器進行申請,也就是說,電子郵箱其實就是用戶在郵件服務器上申請的賬戶。郵件服務器會把接收到的郵件保存到爲該賬戶所分配的郵箱空間中,用戶通過用戶名密碼登錄到郵件服務器查收該地址已經收到的郵件。一般來講,郵件服務器爲用戶分配的郵箱空間是有限的。

郵件客戶端

我們可以直接在網站上進行郵件收發,也可以使用常見的FoxMail、Outlook等郵件客戶端軟件接受郵件。郵件客戶端軟件通常集郵件撰寫、發送和收發功能於一體,主要用於幫助用戶將郵件發送給SMTP郵件服務器和從POP3/IMAP郵件服務器讀取用戶的電子郵件。

郵件傳輸協議

電子郵件需要在郵件客戶端和郵件服務器之間,以及兩個郵件服務器之間進行郵件傳遞,那就必須要遵守一定的規則,這個規則就是郵件傳輸協議。下面我們分別簡單介紹幾種協議:

  1. SMTP協議:全稱爲 Simple Mail Transfer Protocol,簡單郵件傳輸協議。它定義了郵件客戶端軟件和SMTP郵件服務器之間,以及兩臺SMTP郵件服務器之間的通信規則。
  2. POP3協議:全稱爲 Post Office Protocol,郵局協議。它定義了郵件客戶端軟件和POP3郵件服務器的通信規則。
  3. IMAP協議:全稱爲 Internet Message Access Protocol,Internet消息訪問協議,它是對POP3協議的一種擴展,也是定義了郵件客戶端軟件和IMAP郵件服務器的通信規則。

郵件格式

要想各種郵件處理程序能識別我們所寫的電子郵件,能從我們所書寫的電子郵件中分析和提取出發件人、收件人、郵件主題和郵件內容以及附件等信息,那麼我們所寫的電子郵件必須要遵循一定的格式要求,而這種郵件內容的基本格式和具體細節分別是由 RFC822 文檔和 MIME 協議定義的。

  1. RFC822 文檔中定義的文件格式包括兩個部分:郵件頭和郵件體。
  2. MIME協議(Multipurpose Internet Mail Extensions )用於定義複雜郵件體的格式,它可以表達多段平行的文本內容和非文本的郵件內容,例如,在郵件體中內嵌的圖像數據和郵件附件等。另外,MIME協議的數據格式也可以避免郵件內容在傳輸過程中發生信息丟失。MIME協議不是對RFC822郵件格式的升級和替代,而是基於RFC822郵件格式的擴展應用。一言以蔽之,RFC822定義了郵件內容的格式和郵件頭字段的詳細細節,MIME協議則是定義瞭如何在郵件體部分表達出的豐富多樣的數據內容。

電子郵件發送和接收流程

電子郵件發送和接收流程

圖示的六個步驟分別進行如下的說明:

①用戶A的電子郵箱爲:[email protected],通過郵件客戶端軟件寫好一封郵件,交到QQ的郵件服務器,這一步使用的協議是SMTP,對應圖示的①;

②QQ郵箱會根據用戶A發送的郵件進行解析,也就是根據收件地址判斷是否是自己管轄的賬戶,如果收件地址也是QQ郵箱,那麼會直接存放到自己的存儲空間。這裏我們假設收件地址不是QQ郵箱,而是163郵箱,那麼QQ郵箱就會將郵件轉發到163郵箱服務器,轉發使用的協議也是SMTP,對應圖示的②;

③163郵箱服務器接收到QQ郵箱轉發過來的郵件,也會判斷收件地址是否是自己,發現是自己的賬戶,那麼就會將QQ郵箱轉發過來的郵件存放到自己的內部存儲空間,對應圖示的③;

④用戶A將郵件發送了之後,就會通知用戶B去指定的郵箱收取郵件。用戶B會通過郵件客戶端軟件先向163郵箱服務器請求,要求收取自己的郵件,對應圖示的④;

⑤163郵箱服務器收到用戶B的請求後,會從自己的存儲空間中取出B未收取的郵件,對應圖示⑤;

⑥163郵箱服務器取出用戶B未收取的郵件後,將郵件發給用戶B,對應圖示的⑥;最後三步用戶B收取郵件的過程,使用的協議是POP3;

電子郵件的使用場景

在系統中電子郵件的使用場景:

  1. 註冊驗證

  2. 營銷推送

  3. 觸發機制

  4. 監控報警

電子郵件是業務和安全的最後一道防線 —— 當系統無法自動處理的時候,通過郵件提醒相關支持人員。

SpringBoot實現發送電子郵件

準備賬號

註冊發件郵箱並設置客戶端授權碼,這裏以163免費郵箱爲例:

163郵箱協議設置-1

163郵箱協議設置-2

構建項目並配置

搭建完項目以後,進行下面的兩步配置。

application.properties配置參數:

# 郵箱配置
spring.mail.host=smtp.163.com
# 你的163郵箱
[email protected]
# 注意這裏不是郵箱密碼,而是SMTP授權密碼
spring.mail.password=isb001
spring.mail.port=25
spring.mail.protocol=smtp
spring.mail.default-encoding=UTF-8

pom.xml依賴spring-boot-starter-mail模塊:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>

實現服務端代碼

MailService.java:

package org.ijiangtao.tech.spring.boot.mail.imail.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;

@Service
public class MailService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${spring.mail.username}")
    private String from;

    @Autowired
    private JavaMailSender mailSender;

    /**
     * 簡單文本郵件
     * @param to 接收者郵件
     * @param subject 郵件主題
     * @param contnet 郵件內容
     */
    public void sendSimpleMail(String to, String subject, String contnet){

        SimpleMailMessage message = new SimpleMailMessage();
        message.setTo(to);
        message.setSubject(subject);
        message.setText(contnet);
        message.setFrom(from);

        mailSender.send(message);
    }

    /**
     * HTML 文本郵件
     * @param to 接收者郵件
     * @param subject 郵件主題
     * @param contnet HTML內容
     * @throws MessagingException
     */
    public void sendHtmlMail(String to, String subject, String contnet) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(contnet, true);
        helper.setFrom(from);

        mailSender.send(message);
    }

    /**
     * 附件郵件
     * @param to 接收者郵件
     * @param subject 郵件主題
     * @param contnet HTML內容
     * @param filePath 附件路徑
     * @throws MessagingException
     */
    public void sendAttachmentsMail(String to, String subject, String contnet,
                                    String filePath) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();

        MimeMessageHelper helper = new MimeMessageHelper(message, true);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(contnet, true);
        helper.setFrom(from);

        FileSystemResource file = new FileSystemResource(new File(filePath));
        String fileName = file.getFilename();
        helper.addAttachment(fileName, file);

        mailSender.send(message);
    }

    /**
     * 圖片郵件
     * @param to 接收者郵件
     * @param subject 郵件主題
     * @param contnet HTML內容
     * @param rscPath 圖片路徑
     * @param rscId 圖片ID
     * @throws MessagingException
     */
    public void sendInlinkResourceMail(String to, String subject, String contnet,
                                       String rscPath, String rscId) {
        logger.info("發送靜態郵件開始: {},{},{},{},{}", to, subject, contnet, rscPath, rscId);

        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = null;

        try {

            helper = new MimeMessageHelper(message, true);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(contnet, true);
            helper.setFrom(from);

            FileSystemResource res = new FileSystemResource(new File(rscPath));
            helper.addInline(rscId, res);
            mailSender.send(message);
            logger.info("發送靜態郵件成功!");

        } catch (MessagingException e) {
            logger.info("發送靜態郵件失敗: ", e);
        }

    }

}

新建郵件模板

我們使用thymeleaf作爲模板引擎。

emailTeplate.html:

<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"/>
    <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
    <title>註冊-測試郵件模板</title>
</head>
<body>
    你好,感謝你的註冊,這是一封驗證郵件,請點擊下面的連接完成註冊,感謝您的支持。
    <a href="#" th:href="@{https://github.com/{id}(id=${id})}">激活賬戶</a>
</body>
</html>

測試發送郵件

測試發送郵件,使用單元測試MailServiceTest.java:

package org.ijiangtao.tech.spring.boot.mail.imail.service;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.annotation.Resource;
import javax.mail.MessagingException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class MailServiceTest {

    @Autowired
    private MailService mailService;

    @Resource
    private TemplateEngine templateEngine;

    @Test
    public void sendSimpleMail() {
        mailService.sendSimpleMail("[email protected]","測試spring boot imail-主題","測試spring boot imail - 內容");
    }

    @Test
    public void sendHtmlMail() throws MessagingException {

        String content = "<html>\n" +
                "<body>\n" +
                "<h3>hello world</h3>\n" +
                "<h1>html</h1>\n" +
                "<body>\n" +
                "</html>\n";

        mailService.sendHtmlMail("[email protected]","這是一封HTML郵件",content);
    }

    @Test
    public void sendAttachmentsMail() throws MessagingException {
        String filePath = "/ijiangtao/軟件開發前景.docx";
        String content = "<html>\n" +
                "<body>\n" +
                "<h3>hello world</h3>\n" +
                "<h1>html</h1>\n" +
                "<h1>附件傳輸</h1>\n" +
                "<body>\n" +
                "</html>\n";
        mailService.sendAttachmentsMail("[email protected]","這是一封HTML郵件",content, filePath);
    }

    @Test
    public void sendInlinkResourceMail() throws MessagingException {
        //TODO 改爲本地圖片目錄
        String imgPath = "/ijiangtao/img/blob/dd9899b4cf95cbf074ddc4607007046c022564cb/blog/animal/dog/dog-at-work-with-computer-2.jpg?raw=true";
        String rscId = "admxj001";
        String content = "<html>" +
                "<body>" +
                "<h3>hello world</h3>" +
                "<h1>html</h1>" +
                "<h1>圖片郵件</h1>" +
                "<img src='cid:"+rscId+"'></img>" +
                "<body>" +
                "</html>";

        mailService.sendInlinkResourceMail("[email protected]","這是一封圖片郵件",content, imgPath, rscId);
    }

    @Test
    public void testTemplateMailTest() throws MessagingException {
        Context context = new Context();
        context.setVariable("id","ispringboot");

        String emailContent = templateEngine.process("emailTeplate", context);
        mailService.sendHtmlMail("[email protected]","這是一封HTML模板郵件",emailContent);

    }
}

測試結果,收到了電子郵件:

總結

SpringBoot-Email

在生產環境,一般郵件服務會單獨部署,並通過HTTP或MQ等方式暴露出來。

郵件部署架構

相關鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章