HTML 郵件中引用圖片需採用 CID (Content-ID) 機制:圖片作為附件部分添加,設置 Content-ID 頭,HTML 中用 <img src="cid:xxx"> 引用。
<img src="..."> 的本地/網絡路徑。MimeBodyPart 添加到 MimeMultipart("related") (related 表示內嵌關係)。setHeader("Content-ID", "<img001>") (尖括號必須)。<img src="cid:img001">。MimeMultipart("mixed") 頂層容器。// 頂層 mixed 容器
MimeMultipart mixed = new MimeMultipart("mixed");
// related 容器:包含 HTML 正文 + 內嵌圖片
MimeMultipart related = new MimeMultipart("related");
// 1. HTML 正文 Part
MimeBodyPart htmlPart = new MimeBodyPart();
htmlPart.setContent(processedHtml, "text/html; charset=UTF-8");
related.addBodyPart(htmlPart);
// 2. 內嵌圖片 Parts
for (int i = 0; i < imageFiles.size(); i++) {
MimeBodyPart imgPart = new MimeBodyPart();
FileDataSource fds = new FileDataSource(imageFiles.get(i));
imgPart.setDataHandler(new DataHandler(fds));
imgPart.setHeader("Content-ID", "<img" + i + ">"); // 關鍵
imgPart.setDisposition(MimeBodyPart.INLINE);
related.addBodyPart(imgPart);
}
// 3. 將 related 整體作為一個 Part 放入 mixed
MimeBodyPart relatedWrapper = new MimeBodyPart();
relatedWrapper.setContent(related);
mixed.addBodyPart(relatedWrapper);
// 4. 普通附件直接加入 mixed
for (File att : attachments) {
MimeBodyPart attPart = new MimeBodyPart();
attPart.setDataHandler(new DataHandler(new FileDataSource(att)));
attPart.setFileName(MimeUtility.encodeWord(att.getName(), "UTF-8", null));
mixed.addBodyPart(attPart);
}
message.setContent(mixed);
Flags.Flag 定義標準標誌:
| 標誌 | 含義 |
|---|---|
ANSWERED | 已回覆 |
DELETED | 標記刪除 (需 folder.close(true) 或 expunge() 生效) |
DRAFT | 草稿 |
FLAGGED | 標星/重要 |
RECENT | 最近到達 (只讀,由服務器設置) |
SEEN | 已讀 |
USER | 用戶自定義關鍵字 |
Message msg = folder.getMessage(1);
// 設置已讀
msg.setFlag(Flags.Flag.SEEN, true);
// 標記刪除
msg.setFlag(Flags.Flag.DELETED, true);
// 批量設置
Flags flags = new Flags(Flags.Flag.ANSWERED, Flags.Flag.FLAGGED);
msg.setFlags(flags, true);
// 檢查
if (msg.isSet(Flags.Flag.SEEN)) { ... }
// 關鍵:expunge() 永久刪除標記為 DELETED 的郵件
folder.close(true); // true 即調用 expunge()
注意:並非所有 IMAP/POP3 服務器支持所有標誌,操作前可用 folder.getPermanentFlags() 檢查。
郵件內容可能是巢狀 multipart/* 結構 (如 mixed → related → alternative),需遞歸處理。
public void parseMessage(Part part) throws MessagingException, IOException {
String contentType = part.getContentType();
String disposition = part.getDisposition(); // PART.ATTACHMENT / PART.INLINE / null
if (part.isMimeType("text/*")) {
// 文本內容 (plain/html)
String text = (String) part.getContent();
System.out.println("【文本】" + text);
}
else if (part.isMimeType("multipart/*")) {
// 容器:遞歸處理子 Part
Multipart mp = (Multipart) part.getContent();
for (int i = 0; i < mp.getCount(); i++) {
parseMessage(mp.getBodyPart(i));
}
}
else {
// 單獨附件 (application/octet-stream, image/*, etc.)
String fileName = part.getFileName();
if (fileName != null) {
fileName = MimeUtility.decodeText(fileName); // 解碼 RFC 2047
}
InputStream is = part.getInputStream();
saveAttachment(fileName, is); // 自行實現保存邏輯
System.out.println("【附件】" + fileName + " (" + contentType + ")");
}
}
Store store = session.getStore("imaps"); // 或 pop3s
store.connect(host, user, password);
Folder folder = store.getFolder("INBOX");
folder.open(Folder.READ_WRITE); // 需寫入權限以設置標誌
Message[] messages = folder.getMessages();
for (Message msg : messages) {
System.out.println("=== 郵件: " + msg.getSubject() + " ===");
parseMessage(msg);
}
// 完成後關閉
folder.close(true);
store.close();
MimeUtility.encodeWord,接收端 MimeUtility.decodeText,主題/附件名/內文均指定 UTF-8。part.getInputStream() 直接寫入磁盤,避免 getContent() 將大文件載入內存導致 OOM。MessagingException 並重試/告警。