在企業級應用開發中,發送包含內嵌圖片的 HTML 郵件是常見需求。本文將詳細介紹如何使用 JavaMail API、XML 模板與 MimeBodyPart 實作完整的內嵌圖片郵件發送功能,包含前端表單、後端控制器與圖片資源管理。
本範例採用經典的 MVC 架構:
{0}~{8} 作為佔位符<form action="mailAction!sendMail" method="post" name="mailForm" id="mailFormId">
<ul class="list-group">
<li class="list-group-item">
<div class="input-group">
<span class="input-group-addon">姓名:</span>
<input type="text" class="form-control" placeholder="your name" name="mailForm.name">
</div>
</li>
<li class="list-group-item">
<div class="input-group">
<span class="input-group-addon">電話:</span>
<input type="text" class="form-control" placeholder="your phone" name="mailForm.phone">
</div>
</li>
<li class="list-group-item">
<div class="input-group">
<span class="input-group-addon">郵件:</span>
<input type="text" class="form-control" placeholder="your e-mail" name="mailForm.e_mail">
</div>
</li>
<li class="list-group-item">
<span class="label label-default">留言:</span>
<br><br>
<textarea rows="10" name="mailForm.content" placeholder="請輸入留言(不超過 500 字)"></textarea>
</li>
<li class="list-group-item">
<center><button onclick="$('#mailFormId').submit();" type="button" class="btn btn-success">發送郵件</button></center>
</li>
</ul>
</form>
模板採用 CID (Content-ID) 方式引用內嵌圖片,cid:{4} 至 cid:{8} 對應不同圖片資源:
<?xml version="1.0" encoding="UTF-8"?>
<xml-body>
<html>
<body>
<table width="100%"><tbody><tr>
<td><table align="center" width="600"><tbody><tr>
<td align="center" bgcolor="#70bbd9">
<img src="cid:{4}" alt="Creating Email Magic" width="300" height="230" />
</td></tr><tr>
<td bgcolor="#ffffff">
<b>發件人: {0} <br/> 電話: {1} <br/> 郵箱: {2} <br/> 內容:<br/></b>
<td>{3}</td>
</tr><tr>
<td><!-- 兩張並排圖片 -->
<img src="cid:{5}" width="100%" height="140" />
<img src="cid:{6}" width="100%" height="140" />
</td>
</tr><tr>
<td bgcolor="#ee4c50"><!-- 社群圖示 -->
<img src="cid:{7}" width="38" height="38" />
<img src="cid:{8}" width="38" height="38" />
</td>
</tr></tbody></table></td></tr></tbody></table>
</body>
</html>
</xml-body>
public class ReaderXML {
public String read(String path) {
return reader(path);
}
private String reader(String path) {
SAXReader reader = new SAXReader();
try {
Document doc = reader.read(new File(path));
Element root = doc.getRootElement();
Element html = root.element("html");
return html.asXML();
} catch (DocumentException e) {
e.printStackTrace();
}
return null;
}
}
關鍵在於 MimeMultipart("related") 組合 HTML 內容與圖片,並透過 setHeader("Content-ID", imageName) 設定圖片唯一標識:
public class MailAction extends ActionSupport {
private MailForm mailForm;
// 建立內嵌圖片 MimeBodyPart
private MimeBodyPart createImageMimeBodyPart(String imageName)
throws MessagingException, UnsupportedEncodingException {
FileDataSource fds = new FileDataSource(
ServletActionContext.getServletContext().getRealPath("/image") + "/" + imageName + ".gif");
MimeBodyPart mbp = new MimeBodyPart();
mbp.setDataHandler(new DataHandler(fds));
mbp.setHeader("Content-ID", imageName); // 關鍵:對應模板中的 cid:{imageName}
mbp.setFileName(MimeUtility.encodeText(fds.getName()));
return mbp;
}
public String sendMail() {
try {
// 1. 讀取 XML 模板並填充資料
String xmlPath = ServletActionContext.getServletContext().getRealPath("/mailTemplate") + "/myMailTemplete.xml";
String template = new ReaderXML().read(xmlPath);
Object[] params = {mailForm.getName(), mailForm.getPhone(), mailForm.getE_mail(),
mailForm.getContent(), "e_mail", "left", "right", "tw", "fb"};
String htmlContent = MessageFormat.format(template, params);
// 2. 設定郵件伺服器 (網易 163)
Properties props = new Properties();
props.put("mail.smtp.host", "smtp.163.com");
props.put("mail.smtp.auth", "true");
Session session = Session.getInstance(props);
session.setDebug(true);
// 3. 建立 MIME 郵件
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("your@163.com"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress("target@qq.com"));
message.setSubject("有人聯繫你---來自個人網站");
message.setSentDate(new Date());
// 4. 組裝 multipart/related 結構
BodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(htmlContent, "text/html;charset=gb2312");
MimeMultipart multipart = new MimeMultipart("related");
multipart.addBodyPart(htmlPart);
// 加入 5 張內嵌圖片
multipart.addBodyPart(createImageMimeBodyPart("e_mail"));
multipart.addBodyPart(createImageMimeBodyPart("left"));
multipart.addBodyPart(createImageMimeBodyPart("right"));
multipart.addBodyPart(createImageMimeBodyPart("tw"));
multipart.addBodyPart(createImageMimeBodyPart("fb"));
message.setContent(multipart);
message.saveChanges();
// 5. 發送
Transport transport = session.getTransport("smtp");
transport.connect("smtp.163.com", "your_username", "your_password");
transport.sendMessage(message, message.getAllRecipients());
transport.close();
return "sendMail";
} catch (Exception e) {
e.printStackTrace();
return "errors";
}
}
}
<img src="cid:xxx"> 配合 MimeBodyPart.setHeader("Content-ID", "xxx") 實現圖片內嵌MimeUtility.encodeText() 避免亂碼