首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JavaMail API 進階實戰:從發送郵件到複雜多媒體郵件解析

     編輯:Java 相關知識

JavaMail API 功能強大但學習曲線陡峭。本文以問題導向方式,系統講解發送純文本/附件郵件、POP3 收信列表/刪除/已讀未讀統計、以及最棘手的 Multipart 郵件 (含 HTML、圖片、附件) 遞迴解析技巧,附完整代碼範例。

一、發送簡單郵件

Properties props = new Properties();
props.put("mail.smtp.host", smtpHost);
Session session = Session.getDefaultInstance(props, null);

MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
message.setSubject("Hello");
message.setSentDate(new Date());
message.setText("郵件正文內容");

Transport.send(message);

二、發送帶附件郵件 (Multipart 機制)

核心:建立多個 MimeBodyPart,組合成 MimeMultipart 設為訊息內容。

MimeBodyPart textPart = new MimeBodyPart();
textPart.setText("正文內容");

MimeBodyPart attachPart = new MimeBodyPart();
attachPart.setText("附件文字內容", "gb2312"); // 或使用 attachFile()

Multipart mp = new MimeMultipart();
mp.addBodyPart(textPart);
mp.addBodyPart(attachPart);
message.setContent(mp);

三、POP3 獲取郵件列表與統計

Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore("pop3");
store.connect(pop3Host, username, password);

Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_ONLY);

Message[] messages = inbox.getMessages();
for (int i = 0; i < messages.length; i++) {
    System.out.println("第 " + i + " 封: " + messages[i].getFrom()[0] + "\t" + messages[i].getSubject());
}

int total = inbox.getMessageCount();
int unread = inbox.getUnreadMessageCount();
System.out.println("總共 " + total + " 封,未讀 " + unread + " 封");

inbox.close();
store.close();

四、刪除郵件

Message msg = folder.getMessage(1);
msg.setFlag(Flags.Flag.DELETED, true); // 標記刪除
// 關閉 Folder 時 expunge=true 才會真正刪除
folder.close(true);

五、Multipart 郵件深度解析 (核心難點)

複雜郵件結構呈樹狀:multipart/alternative (純文本+HTML 替代) 內嵌於 multipart/related (含圖片) 中。關鍵洞見:第一層 Part 常非直接內容,需遞迴遍歷至葉節點判斷 MIME 類型。

Multipart 郵件結構

典型 Multipart 嵌套結構

遞迴解析核心邏輯


public String getPart(Part part, int partNum, int msgNum, int displayMode) 
        throws MessagingException, IOException {
    String contentType = part.getContentType();
    if (contentType == null) return "part 無效";

    ContentType ct = new ContentType(contentType);
    
    // 葉節點:直接內容
    if (ct.match("text/html") || ct.match("text/plain")) {
        return "
" + (String)part.getContent() + "
"; } // 附件處理 if (partNum != 0) { String fileName = part.getFileName(); if (fileName != null) { // 可儲存附件: storeFile(fileName, part.getInputStream()); return "附件: " + fileName + "
"; } } // 遞迴處理 multipart/alternative (選擇顯示模式: 1=HTML, 2=Plain) if (part.isMimeType("multipart/alternative")) { Multipart mp = (Multipart)part.getContent(); String htmlContent = "", plainContent = ""; for (int i = 0; i < mp.getCount(); i++) { BodyPart bp = mp.getBodyPart(i); if (bp.isMimeType("text/html")) htmlContent = getPart(bp, i, 3, 1); if (bp.isMimeType("text/plain")) plainContent = getPart(bp, i, 3, 2); } return (displayMode == 1) ? htmlContent : plainContent; } // 其他 multipart 類型 (如 related/mixed) 遞迴遍歷 if (part.isMimeType("multipart/*")) { Multipart mp = (Multipart)part.getContent(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < mp.getCount(); i++) { sb.append(getPart(mp.getBodyPart(i), i, msgNum, displayMode)); } return sb.toString(); } return ""; }

六、附件儲存工具方法


public static void storeFile(String fileName, InputStream in) throws IOException {
    File file = new File(fileName);
    for (int i = 0; file.exists(); i++) file = new File(fileName + i); // 避免覆蓋
    try (FileOutputStream fos = new FileOutputStream(file);
         BufferedOutputStream bos = new BufferedOutputStream(fos);
         BufferedInputStream bis = new BufferedInputStream(in)) {
        byte[] buffer = new byte[1024];
        int len;
        while ((len = bis.read(buffer)) != -1) bos.write(buffer, 0, len);
    }
}

七、實戰建議

  • 發送端:善用 MimeMultipart 組合豐富內容
  • 接收端:必須遞迴解析,不可假設固定結構
  • 編碼處理:注意 setText(text, charset) 及附件檔名編碼 (RFC 2231)
  • 大量郵件:考慮使用 FetchProfile 預取標題避免逐封解析完整內容
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved