發送郵件是相當常見的功能,許多網路平台光是註冊會員就要寄確認信給使用者了。或者是忘記密碼、購物網站下單、銀行的對帳單等情境,也都會透過 email 來通知。
本文將示範在 Spring Boot 專案串接電子郵件服務,接著發送純文字郵件,並攜帶附件。而後續的文章則會示範如何發送內嵌圖片的 HTML 郵件。
在串接外部服務時,通常都需要那個平台的帳密,郵件服務也不例外。筆者在本文選擇採用 Gmail 來寄信。然而 Google 為了安全性考量,不讓我們直接在程式中使用 Google 密碼。因此接下來請依照指示,產生「應用程式密碼」。
在 Google 帳戶的首頁,前往「安全性」的頁籤,找到「登入 Google 的方式」,進入「兩步驟驗證」畫面。
從畫面最下方,進入「應用程式密碼」畫面。
在畫面中為這次要建立的密碼取個名字。
按下「建立」後,即可得到 16 個字的應用程式密碼。請讀者將它記在別的地方,因為離開此畫面後,便無法再看到了。
完成取得應用程式密碼的前置工作後,就能準備程式專案了。筆者使用的 Java 版本為 17(zulu-17);Spring Boot 版本為 3.1.3;建置工具使用 Maven。
請在 pom.xml 檔案添加「Spring Mail」的依賴。
<!-- Spring Boot Mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
另外在後面的實作,我們會在 Spring 元件中撰寫程式邏輯。因此請把「Spring Web」的依賴也添加進來。
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
請在 application.properties 檔案,添加以下參數。
# 郵件服務主機
spring.mail.host=smtp.gmail.com
spring.mail.port=587
# 郵件服務帳密
spring.mail.username=YOUR_GOOGLE_ACCOUNT@gmail.com
spring.mail.password=YOUR_APPLICATION_PASSWORD
# 傳輸協定
spring.mail.properties.mail.transport.protocol=smtp
# 是否向郵件服務驗證身份
spring.mail.properties.mail.smtp.auth=true
# 是否啟用 TLS(傳輸層安全),對通訊加密
spring.mail.properties.mail.smtp.starttls.enable=true
其中 spring.mail.username
請提供要用來寄信的 Gmail 地址;而 spring.mail.password
則提供第一節所產生的應用程式密碼。
以下建立了一個專門用來寄信的 Spring 元件。
@Service
public class MailService {
@Autowired
private JavaMailSender mailSender;
// TODO
}
JavaMailSender
是一個介面,Spring Mail 的函式庫會讀取前面在 application.properteis 寫下的參數,自行建立實作類(JavaMailSenderImpl
)後注入進來。它其實是對 Java Mail 進行了封裝,讓我們容易使用。
準備就緒後,接下來讓我們實作發送純文字郵件的程式。以下設計的方法,分別接收了收件地址,以及郵件的主旨與內容。
@Service
public class MailService {
@Autowired
private JavaMailSender mailSender;
public void sendPlainText(Collection<String> receivers, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(receivers.toArray(new String[0]));
message.setSubject(subject);
message.setText(content);
mailSender.send(message);
}
}
以上建立了 SimpleMailMessage
物件,並將相關資訊設置進去。然後呼叫 JavaMailSender
的 send
方法就能發送了。
其中 setTo
方法是用來設置一至多個收件地址,此外讀者亦可選擇 setCC
、setBcc
等方法。
SimpleMailMessage
還有一個 setFrom
方法,是用來設置寄件者。雖然已經在 application.properties 定義了,但收件人所看到的寄件人,就僅僅只是 email 地址。而不像我們平常收到的信,通常會是一個名字。
若想在寄件人顯示自定義的名字,可在使用 setFrom
方法時,採用 名字<寄件人地址>
格式的寫法。
message.setFrom("Vincent Zheng<xxx@gmail.com>");
下圖是寄件人顯示 email 和自定義名字的差別。
事實上,以前的網路標準並不允許傳送 ASCII 以外的字元,也不接受附件。後來新的標準被提出,叫做「多用途網際網路郵件擴展」(Multipurpose Internet Mail Extensions,MIME),之後才開放這麼做。
在 email 中攜帶附件是現今常見的需求。本節先介紹主要的寄信邏輯,再說明如何加入附件。
以下設計的方法,與第三節的純文字相比,新增了 DataSource
的集合。雖然 DataSource
是一個介面,但它的實作類別會裝載附件的資料。
@Service
public class MailService {
@Autowired
private JavaMailSender mailSender;
public void sendPlainTextWithAttachments(Collection<String> receivers, String subject, String content, Collection<DataSource> attachments) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo(receivers.toArray(new String[0]));
helper.setSubject(subject);
helper.setText(content);
for (DataSource source : attachments) {
helper.addAttachment(source.getName(), source);
}
mailSender.send(message);
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
首先建立一個 MimeMessage
,它在最後會被 JavaMailSender
發送。
接著建立 MimeMessageHelper
,並將 MimeMessage
傳入。這個 helper 能幫助我們對郵件設置一些資料。其中附件是透過 addAttachment
方法來添加,需要傳入在郵件中要顯示的名稱,以及附件資料本身。
附件是以 DataSource
作為在程式碼中傳遞的介面。下面將介紹如何將附件資料轉換成這種形式。
有些附件可能是放在 local 伺服器上的,所以每個人收到的其實都一樣。例如公司 logo 或 QR code 的圖片。
以下的方法是將名為「logo.jpg」的檔案當成附件,回傳了 FileDataSource
。
private DataSource getTestFileDataSource() {
return new FileDataSource("logo.jpg");
}
FileDataSource
也提供以 File
為參數的建構子。
private DataSource getTestFileDataSource() {
new FileDataSource(new File("logo.jpg"));
}
有些文件是要透過程式來生成的,例如匯出交易紀錄、電子發票的 PDF 檔,甚至是從雲端空間下載的產品圖片。然而它們可能僅止於程式碼中的變數而已(如 FileOutputStream
、BufferedImage
等),不一定會被輸出成檔案。
面對這種情況,可以使用 ByteArrayDataSource
。只要取得位元組陣列(byte[]
),並且知道 MIME 檔案類型即可。
下面提供一個範例,是取得 BufferedImage
的位元組陣列,轉換成 ByteArrayDataSource
。
private DataSource getTestByteArrayDataSource() {
BufferedImage image = ImageIO.read(new File("qr_code.jpg"));
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "jpg", os);
byte[] bytes = os.toByteArray();
ByteArrayDataSource imageSource = new ByteArrayDataSource(bytes, "image/jpeg");
imageSource.setName("invoice.png");
}
至於 ByteArrayDataSource
建構子的第二個參數,是 MIME 檔案類型。可參考「常見 MIME 類型列表」,給予適當的值。
Ref:SpringBoot - 第二十七章 | JavaMailSender發送信件
今日文章到此結束!
最後推廣一下自己的部落格,我是「新手工程師的程式教室」的作者,請多指教