1. 概述
虽然图片通常以附件形式添加到邮件中,但也可以直接嵌入邮件正文。这种方式特别适合需要直接展示视觉内容的场景,让收件人无需单独下载即可查看图片。
Java Mail API 通过 MimeBodyPart
和 MimeMultipart
类提供了在邮件正文中嵌入图片的机制。
本教程将探讨如何使用这两个类实现图片内联,包括如何通过内容ID(Content-ID)识别图片并在HTML正文中引用该ID。最后,我们还会使用GreenMail库作为模拟SMTP服务器编写单元测试。
2. 项目配置
首先用Maven创建一个简单的Java项目。在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.example.com";
private final String PORT = "587";
⚠️ 这里可以使用任何支持标准SMTP协议的服务,比如Amazon SES或Azure Communication Service。
最后将示例图片 java.png
放到项目的 resources
目录下。
3. 使用 MimeMultipart
和 MimeBodyPart
内联图片
发送邮件需要几个关键步骤:定义SMTP连接属性 → 创建会话 → 构建多部分消息 → 发送邮件。
3.1. 定义连接属性
创建 smtpProperties()
方法封装SMTP配置:
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;
}
这里通过键值对配置了SMTP认证、TLS加密和服务器地址等基础参数。
3.2. 创建会话实例
使用配置属性创建认证会话:
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服务器的连接,需要传入连接属性和认证凭据。
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("sender@example.com"));
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正文和图片部分 - 为图片设置唯一ID(
<image1>
)和内联 disposition - ✅ 使用
cid:
协议在HTML中引用图片ID - 用
related
类型的MimeMultipart
组合各部分
踩坑提示:如果需要内联多张图片,必须为每张图片创建独立的
MimeBodyPart
并分配唯一ID。
3.4. 调用示例
实际发送邮件的代码:
InlineImage inlineImage = new InlineImage();
Properties properties = inlineImage.smtpProperties();
Session session = inlineImage.smtpsession(properties);
String to = "receiver@example.com";
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);
⚠️ HTML中必须使用 cid:image1
格式引用图片,如果 cid
前缀缺失或ID不匹配,图片会变成附件而非内联显示。
4. 单元测试
直接使用生产SMTP服务器测试很麻烦,用GreenMail模拟服务器更高效。
4.1. 创建测试会话
为GreenMail定制会话创建方法:
Session smtpsession(GreenMail greenMail) {
Session session = greenMail.getSmtp().createSession();
return session;
}
4.2. 测试用例
完整测试代码:
@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正文 + 图片)
- HTML部分包含
cid:image1
引用 - 图片部分的 disposition 确认为
INLINE
5. 总结
本文通过多部分消息结构实现了邮件图片内联,核心在于:
- 为图片分配唯一Content-ID
- 使用
cid:
URI在HTML中引用该ID - 用
related
类型的MimeMultipart
组合内容
完整示例代码可在 GitHub仓库 获取。