使用 Java Mail 在電子郵件中嵌入圖像
1.概述
雖然圖片通常以文件附件的形式添加到電子郵件中,但也可以將其直接嵌入到郵件正文中。當我們想要以內聯方式顯示視覺內容時,這種方法非常有用,它允許收件人直接查看圖片,而無需單獨下載。
Java Mail API 提供了一種使用MimeBodyPart
和MimeMultipart
類別在郵件正文中嵌入或內聯圖像的機制。
在本教程中,我們將探索如何使用MimeMultipart
和MimeBodyPart
類內嵌影像。此外,我們還將學習如何使用內容 ID 識別圖像,以及如何從 HTML 郵件正文中引用該 ID。最後,我們將使用 GreenMail 函式庫作為模擬 SMTP 伺服器編寫單元測試。
2. 項目設定
首先,我們使用 Maven 作為建置工具,建立一個簡單的 Java 應用程式。接下來,我們將[angus-mail](https://mvnrepository.com/artifact/org.eclipse.angus/angus-mail) –
Jakarta Mail API 規範的實作)和[greenmail](https://mvnrepository.com/artifact/com.icegreen/greenmail)
依賴項新增到pom.xml
中:
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>com.icegreen</groupId>
<artifactId>greenmail</artifactId>
<version>2.1.3</version>
</dependency>
angus-mail
提供了MimeMessage
和MimeMultipart
等類別來建立包含多個部分的電子郵件訊息。然後, greenmail
允許我們啟動一個模擬 SMTP 伺服器進行測試。
另外,讓我們定義與 SMTP 伺服器建立連線所需的連線屬性:
private final String USERNAME = "YOUR_SMTP_USERNAME";
private final String PASSWORD = "YOUR_SMTP_PASSWORD";
private final String HOST = "SMTP HOST";
private final String PORT = "SMTP PORT";
值得注意的是,我們可以使用 Amazon SES、Azure 通訊服務或任何其他支援標準 SMTP 協定的供應商提供的 SMTP 服務。
最後,讓我們將java.png
檔案放在我們的專案resources
目錄中。
3. 使用MimeMultipart
和MimeBodyPart
內嵌影像
發送電子郵件需要幾個步驟。首先,我們需要定義一個Properties
對象,其中包含 SMTP 伺服器的連線憑證。然後,我們需要建立一個Session
物件來建立與 SMTP 伺服器的連線。
3.1. 定義Properties
首先,讓我們建立一個名為smtpProperties()
的方法來包含連接詳細資訊:
Properties smtpProperties() {
Properties prop = new Properties();
prop.put("mail.smtp.auth", "true");
prop.put("mail.smtp.starttls.enable", "true");
prop.put("mail.smtp.host", HOST);
prop.put("mail.smtp.port", PORT);
prop.put("mail.smtp.user", USERNAME);
prop.put("mail.smtp.password", PASSWORD);
return prop;
}
在上面的程式碼中,我們定義了一個Properties
對象,其中包含 SMTP 連線詳細資訊作為鍵值對。
3.2. 定義Session
實例
接下來,讓我們建立一個傳回已配置的Session
物件的方法:
Session smtpsession(Properties props) {
final String username = props.getProperty("mail.smtp.user");
final String password = props.getProperty("mail.smtp.password");
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
return session;
}
這裡,我們使用Session
類別建立 SMTP 會話,該會話允許我們與 SMTP 伺服器建立連線以進行電子郵件傳輸。 session
物件接受連線屬性和驗證憑證作為參數。
3.3. 發送帶有內嵌影像的電子郵件
接下來,讓我們定義一個方法來傳送一條在訊息正文中內嵌圖像的訊息:
void sendEmail(Session session, String to, String subject, String body, String filePath)
throws MessagingException, IOException {
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("[email protected]"));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
message.setSubject(subject);
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(body, "text/html");
MimeBodyPart imagePart = new MimeBodyPart();
imagePart.attachFile(new File(filePath));
imagePart.setContentID("<image1>");
imagePart.setDisposition(MimeBodyPart.INLINE);
MimeMultipart mimeMultipart = new MimeMultipart("related");
mimeMultipart.addBodyPart(htmlPart);
mimeMultipart.addBodyPart(imagePart);
message.setContent(mimeMultipart);
Transport.send(message);
}
在上面的程式碼中,我們使用MimeBodyPart
實例建立了一封包含兩個相互關聯部分的電子郵件。第一部分是 HTML 郵件正文,第二部分是圖片部分。
建立imagePart
物件後,我們呼叫其attachFile()
方法來新增映像檔。然後,我們呼叫setContentID()
方法為圖像檔案分配一個唯一的 ID。此 ID 允許我們使用cid:
協定在 HTML 部分中引用該圖像。此外,我們呼叫setDisposition()
方法將圖像顯示在郵件正文中,而不是作為附件顯示。
最後,我們透過建立一個related
子類型的MimeMultipart
物件來組合這些部分。需要注意的是,在需要嵌入多張圖片的情況下,我們必須為每張圖片建立一個單獨的部分,並賦予其唯一的內容 ID。
3.4. 呼叫方法
接下來我們發送一則訊息:
InlineImage inlineImage = new InlineImage();
Properties properties = inlineImage.smtpProperties();
Session session = inlineImage.smtpsession(properties);
String to = "[email protected]";
String subject = "Baeldung";
String body = """
<p>Welcome to Baeldung, home of Java and its frameworks.</p>
<img src='cid:image1'></img>
<p> Explore and learn. </p>
""";
String imagePath = "src/main/resources/image/java.png";
inlineImage.sendEmail(session, to, subject, body, imagePath);
在上面的程式碼中,我們建立了一個InlineImag
e 對象,檢索了 SMTP 屬性,並初始化了一個郵件會話。
此外,我們定義電子郵件的 HTML 正文,並使用<img src='cid:image1/>
標籤來引用圖片。 cid cid
對應到我們在sendEmail()
方法中指派的 Content-ID。
值得注意的是,如果cid
關鍵字缺失或不正確,則影像將被視為附件。
電子郵件的形式如下:
4.單元測試
使用生產環境的 SMTP 伺服器編寫單元測試通常不太方便。我們可以在測試過程中使用模擬 SMTP 伺服器來模擬電子郵件投遞。
讓我們定義另一個回傳Session
物件的方法,這次接受一個GreenMail
實例:
Session smtpsession(GreenMail greenMail) {
Session session = greenMail.getSmtp()createSession();
return session;
}
在這裡,我們使用 GreenMail 內建的 SMTP 服務建立一個 SMTP 會話,這有助於測試電子郵件功能而無需發送實際電子郵件。
接下來,讓我們為sendEmail()
方法寫一個單元測試:
@Test
void givenHtmlEmailWithInlineImage_whenSentViaGreenMailSmtp_thenReceivesEmailWithInlineImage() throws Exception {
GreenMail greenMail = new GreenMail(ServerSetupTest.SMTP);
greenMail.start();
InlineImage inlineImage = new InlineImage();
Session session = inlineImage.smtpsession(greenMail);
String to = "receiver@localhost";
String subject = "Test Subject";
String body = """
<p>Welcome to Baeldung, home of Java and its frameworks.</p>
<img src='cid:image1'></img>
<p> Explore and learn. </p>
""";
String imagePath = "src/main/resources/image/java.png";
inlineImage.sendEmail(session, to, subject, body, imagePath);
MimeMessage[] receivedMessages = greenMail.getReceivedMessages();
assertEquals(1, receivedMessages.length);
MimeMessage message = receivedMessages[0];
Multipart multipart = (Multipart) message.getContent();
assertEquals(2, multipart.getCount());
BodyPart htmlPart = multipart.getBodyPart(0);
assertTrue(htmlPart.getContentType().contains("text/html"));
String htmlContent = (String) htmlPart.getContent();
assertTrue(htmlContent.contains("cid:image1"));
BodyPart imagePart = multipart.getBodyPart(1);
assertEquals(Part.INLINE, imagePart.getDisposition());
greenMail.stop();
}
在上面的程式碼中,我們驗證發送的電子郵件包含兩個部分——HTML 正文和內嵌圖像。此外,我們還檢查了:
- 第一部分的內容類型是
text/html
- 正文包含對
cid:image1
引用 - 圖像部分被標記為
INLINE
,而不是附件
5. 結論
在本文中,我們學習如何透過在電子郵件中定義多個部分並為圖像分配唯一的 Content-ID 來發送帶有內聯圖像的電子郵件。此外,我們也了解如何使用cid:
URI 方案在郵件正文中引用圖像。
與往常一樣,範例的完整原始程式碼可 在 GitHub 上找到。