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

JavaMail API 核心應用指南:從郵件發送到複雜附件處理

     編輯:Java 相關知識

JavaMail 架構核心

JavaMail (Jakarta Mail) 採用工廠模式與協議提供者分離設計:

  • Session:環境上下文,持有屬性、認證器、Provider 註冊表。
  • Transport:發送協議抽象 (SMTP, SMTP SSL, SMTP STARTTLS)。
  • Store:接收協議抽象 (IMAP, POP3, IMAP SSL)。
  • Message:郵件模型 (MimeMessage 支持 MIME 多部分、附件、HTML)。

發送郵件完整流程

public class MailSender {
    private final Session session;
    public MailSender(String host, int port, String user, String pass, boolean ssl) {
        Properties props = new Properties();
        props.put("mail.smtp.host", host);
        props.put("mail.smtp.port", port);
        props.put("mail.smtp.auth", "true");
        if (ssl) {
            props.put("mail.smtp.ssl.enable", "true");
            props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        } else {
            props.put("mail.smtp.starttls.enable", "true"); // STARTTLS 升級加密
        }
        this.session = Session.getInstance(props, new Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(user, pass);
            }
        });
        this.session.setDebug(false); // 生產關閉
    }

    public void send(MailMessage mail) throws MessagingException {
        MimeMessage msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(mail.getFrom()));
        msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mail.getTo()));
        msg.setSubject(mail.getSubject(), "UTF-8");
        msg.setSentDate(new Date());

        // 構建多部分內容:文本 + HTML + 附件
        Multipart mp = new MimeMultipart("mixed");

        // 1. 文本/HTML 替代部分
        MimeMultipart alt = new MimeMultipart("alternative");
        alt.addBodyPart(textPart(mail.getTextContent()));
        if (mail.getHtmlContent() != null) alt.addBodyPart(htmlPart(mail.getHtmlContent()));
        MimeBodyPart altPart = new MimeBodyPart();
        altPart.setContent(alt);
        mp.addBodyPart(altPart);

        // 2. 附件
        for (Attachment att : mail.getAttachments()) {
            mp.addBodyPart(attachmentPart(att));
        }

        msg.setContent(mp);
        Transport.send(msg);
    }

    private MimeBodyPart textPart(String text) throws MessagingException {
        MimeBodyPart p = new MimeBodyPart();
        p.setContent(text, "text/plain; charset=UTF-8");
        return p;
    }
    private MimeBodyPart htmlPart(String html) throws MessagingException {
        MimeBodyPart p = new MimeBodyPart();
        p.setContent(html, "text/html; charset=UTF-8");
        return p;
    }
    private MimeBodyPart attachmentPart(Attachment att) throws Exception {
        MimeBodyPart p = new MimeBodyPart();
        DataSource ds = new ByteArrayDataSource(att.getData(), att.getMimeType());
        p.setDataHandler(new DataHandler(ds));
        p.setFileName(MimeUtility.encodeText(att.getFilename(), "UTF-8", "B")); // RFC 2231 編碼
        p.setDisposition(Part.ATTACHMENT);
        return p;
    }
}

關鍵進階主題

1. 複雜附件:內嵌圖片 (CID)

// HTML 中引用 <img src="cid:logo">
MimeBodyPart imgPart = new MimeBodyPart();
imgPart.attachFile(new File("logo.png"));
imgPart.setContentID("<logo>"); // CID
imgPart.setDisposition(MimeBodyPart.INLINE);
related.addBodyPart(imgPart); // 放入 "related" 類型 Multipart

2. 大附件流式處理 (避免 OOM)

// 使用 FileDataSource + SharedByteArrayInputStream 或自定義 DataSource
// 關鍵:不將整個文件讀入 byte[],而是流式輸出
DataSource ds = new FileDataSource(largeFile) {
    public InputStream getInputStream() throws IOException {
        return new BufferedInputStream(new FileInputStream(largeFile), 8192);
    }
};

3. 連接池與異步發送

  • JavaMail 原生無連接池,可用 Apache Commons Pool 封裝 Transport 對象池。
  • 異步化:CompletableFuture.runAsync(() -> sender.send(mail)) 或接入消息隊列 (Kafka/RocketMQ) 解耦。

4. 安全加固清單

風險對策
憑證驗證繞過嚴禁 mail.smtp.ssl.trust=*,生產必須配置有效 CA 或導入信任庫
郵件注入 (Header Injection)所有用戶輸入 (Subject, To, Body) 經過 MimeUtility.encodeText 或白名單過濾換行符
敏感信息日誌洩露session.setDebug(false),生產禁用协议級日誌
中間人攻擊強制 STARTTLS 或 SMTPS,設置 mail.smtp.ssl.enable=true 或 mail.smtp.starttls.required=true

接收郵件 (IMAP) 要點

Store store = session.getStore("imaps"); // IMAP SSL
store.connect(host, user, pass);
Folder inbox = store.getFolder("INBOX");
inbox.open(Folder.READ_WRITE);
// 搜索未讀
Message[] msgs = inbox.search(new FlagTerm(new Flags(Flags.Flag.SEEN), false));
for (Message m : msgs) {
    parseMessage(m); // 遞歸解析 Multipart
    m.setFlag(Flags.Flag.SEEN, true); // 標記已讀
}
inbox.close(true);
store.close();

總結

JavaMail 雖屬老牌 API,但在企業級通知、報表分發、郵件網關場景仍不可替代。掌握 MimeMultipart 類型嵌套規則 (mixed/alternative/related)、流式附件處理、安全加固與異步架構,是構建穩健郵件服務的關鍵。建議封裝為 MailService 領域服務,統一異常處理、重試策略 (指數退避)、指標監控 (發送成功率、延遲、隊列積壓)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved