1. 程式人生 > >補習系列(12)-springboot 與郵件傳送

補習系列(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是一個封裝了郵件功能的元件,依賴如下:

<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

//收發人
[email protected]
[email protected]

//啟用鑑權
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模板,
有時候我們需要在郵件中顯示一張圖片,怎麼辦呢?

  1. 使用img標籤,並指定一個線上的圖片;
    此方案比較通用,應該說大多數線上平臺都採用這種做法,但這麼做的前提是需要有一個統一的圖片儲存及訪問系統。

  2. 使用 Base64編碼,在頁面中嵌入編碼後的內容:

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

該方案非通用,在實測中發現Outlook 無法展示這類標籤,客戶端並未支援。
下面列舉了支援內嵌圖片展示的一些郵件客戶端:

  1. 採用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*[email protected]" ALT="IETF logo">

--boundary-example-1

Content-ID: <foo4*[email protected]>
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>

最終,傳送郵件可支援圖片的展示,如下

參考文件

歡迎繼續關注"美碼師的補習系列-springboot篇" ,期待更多精彩內容^-^