JavaMail發送郵件(超詳細)


一:郵件發送的基本概念

  本文我將闡述使用JavaMail方式發送和接收Email的詳細說明,本博客本着以后遇到類似的郵件發送需求可以直接把代碼粘過去直接使用,快捷方便省時間,對於剛接觸的JavaMail的朋友們還是把文章過一遍,雖然本文不是最好的,但是我可以保證你能成功發送郵件;

  關於郵件還有一些基本知識我將在下面簡單介紹,關於郵件發送和接收的流程可以參考 郵件基本概念及發送方式

1:郵件中的幾個名詞

發件人:
    指的是用哪個郵箱進行發送郵件的人
收件人:
    指的是接收發件人發過來郵件的人,代表這封郵件面向的讀者。可以是零個到多個。
抄送人:
    指的是發件人把郵件發送給收件人的同時並抄送一份發給抄送人;此時抄送人可以看到收件人、抄送人的郵箱
密送人:
    指的是發件人把郵件發送給收件人的同時並抄送一份發給密送人;此時抄送人可以看到收件人、抄送人的郵箱,無法看到密送人的郵箱
說明:抄送人和密送人一般用於項目組A給項目組B發送郵件確認流程,這時項目組A還要告知領導我已經把方案流程發送給項目組B了; 這時我就要把流程方案抄送一份給領導,就可以用到抄送和密送,此郵件領導是不用回復的

2:發送郵件的幾種方式

JavaMail 具體使用說明參考Oracle官網給出的API:鏈接地址

Jakarta Mail 具體使用說明參考Jakarta官方給出的API:鏈接地址

1:javax.*
    也是java標准的一部分,但是沒有包含在標准庫中,一般屬於標准庫的擴展。通常屬於某個特定領域,不是一般性的api。
    所以以擴展的方式提供api,以避免jdk的標准庫過大。當然某些早期的javax,后來被並入到標准庫中,所有也應該屬於新版本JDK的標准庫。
    比如jmx,java5以前是以擴展方式提供,但是jdk5以后就做為標准庫的一部分了,所有javax.management也是jdk5的標准庫的一部分。
2:com.sun.*
    是sun的hotspot虛擬機中java.* 和javax.*的實現類。因為包含在rt中,所以我們也可以調用。但是因為不是sun對外公開承諾的接口,
    所以根據根據實現的需要隨時增減,因此在不同版本的hotspot中可能是不同的,而且在其他的jdk實現中是沒有的,調用這些類,
   可能不會向后兼容,一般不推薦使用

下面介紹的發送郵件的幾種方式:  ①:使用javax.mail的坐標依賴包(導包時看清楚是不是javax.mail的)
        <!--JavaMail基本包-->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
        <!--郵件發送的擴展包-->
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        注:坐標必須為兩個,因為javax.mail沒有攜帶依賴包javax.activation(發送)  ②:使用com.sun.mail的坐標依賴包(不推薦使用,這里我沒講了,下面是它的坐標,坐標內攜帶javax.activation依賴)
        <!-- https://mvnrepository.com/artifact/com.sun.mail/javax.mail -->
        <!--使用Sun提供的Email工具包-->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.2</version>
        </dependency>
    ③:使用Jakarta Mail發送郵件(和javax.mail使用方式基本一樣,不過它內部攜帶了javax.activation依賴包)
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>jakarta.mail</artifactId>
            <version>1.6.7</version>
        </dependency>
  ③:使用SpringBoot集成郵件發送跳轉此博客
    注:SpringBoot集成郵件發送底層使用Jakarta Mail技術 注:javax.JavaMail最后一個版本發布於2018年8月,后期發送郵件最好使用Jakarta Mail(它是javaMail的前身);

總結:不借助SpringBoot的情況下使用 javax.JavaMail 或 Jakarta Mail 方式

二:JavaMailAPI簡單說明

1:Session類

  javax.mail.Session類用於定義整個應用程序所需的環境信息,以及收集客戶端與郵件服務器建立網絡連接的會話信息,例如郵件服務器的主機名、端口號、采用的郵件發送和接收協議等。Session 對象根據這些信息構建用於郵件收發的Transport和Store對象,以及為客戶端創建Message對象時提供信息支持。

Session getInstance(Properties props)
Session getInstance(Properties props, Authenticator authenticator)
說明:獲取一個新的Session對象
    參數:
        props:為Session會話域提供默認值
             mail.store.protocol:接收郵件時分配給協議的名稱
             mail.transport.protocol:發送郵件時分配給協議的名稱
             mail.host:郵箱服務器地址
             mail.user:發件人名稱
             mail.from:發件人郵箱
        authenticator:
            用於在需要用戶名和密碼時回調應用程序

2:Message類

  javax.mail.Message類是創建和解析郵件的核心API,這是一個抽象類,通常使用它的子類javax.mail.internet.MimeMessage類。它的實例對象表示一份電子郵件。客戶端程序發送郵件時,首先使用創建郵件的JavaMail API創建出封裝了郵件數據的Message對象,然后把這個對象傳遞給郵件發送API(Transport 類) 發送,客戶端程序接收郵件時,郵件接收API把接收到的郵件數據封裝在Message類的實例中,客戶端程序在使用郵件解析API從這個對象中解析收到的郵件數據。

3:Transport類

  javax.mail.Transport類是發送郵件的核心API類,它的實例對象代表實現了某個郵件發送協議的郵件發送對象,例如SMTP協議,客戶端程序創建好Message對象后,只需要使用郵件發送API得到Transport對象,然后把Message對象傳遞給Transport 對象,並調用它的發送方法,就可以把郵件發送給指定的SMTP服務器。

4:Store類

  javax.mail.Store類是接收郵件的核心API類,它的實例對象代表實現了某個郵件接收協議的郵件接收對象,例如POP3協議,客戶端程序接收郵件時,只需要使用郵件接收API得到Store對象,然后調用Store對象的接收方法,就可以從指定的POP3服務器獲得郵件數據,並把這些郵件數據封裝到表示郵件的 Message 對象中

三:使用javax中的JavaMail

1:基本坐標導入

        <!--JavaMail基本包-->
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4.7</version>
        </dependency>
        <!--郵件發送的擴展包-->
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>

2:使用JavaMail發送HTML格式郵件

public class JavaxJavaMailClient {

    public String emailHost = "smtp.163.com";       //發送郵件的主機
    public String transportType = "smtp";           //郵件發送的協議
    public String fromUser = "antladdie";           //發件人名稱
    public String fromEmail = "antladdie@163.com";  //發件人郵箱
    public String authCode = "xxxxxxxxxxxxxxxx";    //發件人郵箱授權碼
    public String toEmail = "xiaofeng504@qq.com";   //收件人郵箱
    public String subject = "電子專票開具";           //主題信息

    @Test
    public void ClientTestA() throws UnsupportedEncodingException, javax.mail.MessagingException {

        //初始化默認參數
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol", transportType);
        props.setProperty("mail.host", emailHost);
        props.setProperty("mail.user", fromUser);
        props.setProperty("mail.from", fromEmail);
        //獲取Session對象
        Session session = Session.getInstance(props, null);
        //開啟后有調試信息
        session.setDebug(true);

        //通過MimeMessage來創建Message接口的子類
        MimeMessage message = new MimeMessage(session);
        //下面是對郵件的基本設置
        //設置發件人:
        //設置發件人第一種方式:直接顯示:antladdie <antladdie@163.com>
        //InternetAddress from = new InternetAddress(sender_username);
        //設置發件人第二種方式:發件人信息拼接顯示:螞蟻小哥 <antladdie@163.com>
        String formName = MimeUtility.encodeWord("螞蟻小哥") + " <" + fromEmail + ">";
        InternetAddress from = new InternetAddress(formName);
        message.setFrom(from);

        //設置收件人:
        InternetAddress to = new InternetAddress(toEmail);
        message.setRecipient(Message.RecipientType.TO, to);

        //設置抄送人(兩個)可有可無抄送人:
        List<InternetAddress> addresses = Arrays.asList(new InternetAddress("1457034247@qq.com"), new InternetAddress("575814158@qq.com"));
        InternetAddress[] addressesArr = (InternetAddress[]) addresses.toArray();
        message.setRecipients(Message.RecipientType.CC, addressesArr);

        //設置密送人 可有可無密送人:
        //InternetAddress toBCC = new InternetAddress(toEmail);
        //message.setRecipient(Message.RecipientType.BCC, toBCC);

        //設置郵件主題
        message.setSubject(subject);

        //設置郵件內容,這里我使用html格式,其實也可以使用純文本;純文本"text/plain"
        message.setContent("<h1>螞蟻小哥祝大家工作順利!</h1>", "text/html;charset=UTF-8");

        //保存上面設置的郵件內容
        message.saveChanges();

        //獲取Transport對象
        Transport transport = session.getTransport();
        //smtp驗證,就是你用來發郵件的郵箱用戶名密碼(若在之前的properties中指定默認值,這里可以不用再次設置)
        transport.connect(null, null, authCode);
        //發送郵件
        transport.sendMessage(message, message.getAllRecipients()); // 發送
    }
}

3:使用JavaMail發送HTML內攜帶圖片郵件格式

public class JavaxJavaMailClient {

    public String emailHost = "smtp.163.com";       //發送郵件的主機
    public String transportType = "smtp";           //郵件發送的協議
    public String fromUser = "antladdie";           //發件人名稱
    public String fromEmail = "antladdie@163.com";  //發件人郵箱
    public String authCode = "xxxxxxxxxxxxxxxx";    //發件人郵箱授權碼
    public String toEmail = "xiaofeng504@qq.com";   //收件人郵箱
    public String subject = "電子專票開具";           //主題信息

    @Test
    public void ClientTestB() throws IOException, javax.mail.MessagingException {

        // 1:初始化默認參數
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol", transportType);
        props.setProperty("mail.host", emailHost);
        props.setProperty("mail.user", fromUser);
        props.setProperty("mail.from", fromEmail);
        // 2:獲取Session對象
        Session session = Session.getInstance(props, null);
        session.setDebug(true);

        // 3:創建MimeMessage對象
        MimeMessage message = new MimeMessage(session);

        // 4:設置發件人、收件人、主題、(內容后面設置)
        String formName = MimeUtility.encodeWord("螞蟻小哥") + " <" + fromEmail + ">";
        InternetAddress from = new InternetAddress(formName);
        message.setFrom(from);
        InternetAddress to = new InternetAddress(toEmail);
        message.setRecipient(Message.RecipientType.TO, to);
        //設置郵件主題
        message.setSubject(subject);
        //郵件發送時間
        message.setSentDate(new Date());

        // 5:設置多資源內容
        //=============== 構建郵件內容:多信息片段關聯郵件 使用Content-Type:multipart/related ===============//
        // 5.1:構建一個多資源的郵件塊 用來把 文本內容資源 和 圖片資源關聯;;;related代表多資源關聯
        MimeMultipart text_img_related = new MimeMultipart("related");
        //text_img_related.setSubType("related");
        //注:這里為什么填寫related的請去查閱Multipart類型或者去文章開頭跳轉我之前上一篇郵件介紹

        // 5.2:創建圖片資源
        MimeBodyPart img_body = new MimeBodyPart();
        DataHandler dhImg = new DataHandler(JavaxJavaMailClient.class.getResource("static/b.png"));
        img_body.setDataHandler(dhImg); //設置dhImg圖片處理
        img_body.setContentID("imgA");  //設置資源圖片名稱ID

        // 5.3:創建文本資源,文本資源並引用上面的圖片ID(因為這兩個資源我做了關聯)
        MimeBodyPart text_body = new MimeBodyPart();
        text_body.setContent("<img src='cid:imgA' width=100/> 我是螞蟻小哥!!","text/html;charset=UTF-8");

        // 5.4:把創建出來的兩個資源合並到多資源模塊了
        text_img_related.addBodyPart(img_body);
        text_img_related.addBodyPart(text_body);
        //===========================================================================================//

        // 6:設置我們處理好的資源(存放到Message)
        message.setContent(text_img_related);

        // 7:保存上面設置的郵件內容
        message.saveChanges();
        // 8:獲取Transport對象
        Transport transport = session.getTransport();
        //9:smtp驗證,就是你用來發郵件的郵箱用戶名密碼(若在之前的properties中指定默認值,這里可以不用再次設置)
        transport.connect(null, null, authCode);
        //10:發送郵件
        transport.sendMessage(message, message.getAllRecipients()); // 發送
    }
}

4:使用JavaMail發送HTML帶圖片+附件格式郵件

public class JavaxJavaMailClient {

    public String emailHost = "smtp.163.com";       //發送郵件的主機
    public String transportType = "smtp";           //郵件發送的協議
    public String fromUser = "antladdie";           //發件人名稱
    public String fromEmail = "antladdie@163.com";  //發件人郵箱
    public String authCode = "xxxxxxxxxxxxxxxx";    //發件人郵箱授權碼
    public String toEmail = "xiaofeng504@qq.com";   //收件人郵箱
    public String subject = "電子專票開具";           //主題信息

    @Test
    public void ClientTestC() throws IOException, javax.mail.MessagingException {

        // 1:初始化默認參數
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol", transportType);
        props.setProperty("mail.host", emailHost);
        props.setProperty("mail.user", fromUser);
        props.setProperty("mail.from", fromEmail);
        // 2:獲取Session對象
        Session session = Session.getInstance(props, null);
        session.setDebug(true);

        // 3:創建MimeMessage對象
        MimeMessage message = new MimeMessage(session);

        // 4:設置發件人、收件人、主題、(內容后面設置)
        String formName = MimeUtility.encodeWord("螞蟻小哥") + " <" + fromEmail + ">";
        InternetAddress from = new InternetAddress(formName);
        message.setFrom(from);
        InternetAddress to = new InternetAddress(toEmail);
        message.setRecipient(Message.RecipientType.TO, to);
        //設置郵件主題
        message.setSubject(subject);
        //郵件發送時間
        message.setSentDate(new Date());
        
        //*****郵件內容攜帶 附件 + (HTML內容+圖片)使用Content-Type:multipart/mixed ******//
        // 5:設置一個多資源混合的郵件塊 設置此類型時可以同時存在 附件和郵件內容  mixed代表混合
        MimeMultipart mixed = new MimeMultipart("mixed");

        // 5.1:創建一個附件資源
        MimeBodyPart file_body = new MimeBodyPart();
        DataHandler dhFile = new DataHandler(JavaxJavaMailClient.class.getResource("static/a.zip"));
        file_body.setDataHandler(dhFile); //設置dhFile附件處理
        file_body.setContentID("fileA");  //設置資源附件名稱ID
        //file_body.setFileName("拉拉.zip");   //設置中文附件名稱(未處理編碼)
        file_body.setFileName(MimeUtility.encodeText("一個附件.zip"));   //設置中文附件名稱

        // 5.2:先把附件資源混合到 mixed多資源郵件模塊里
        mixed.addBodyPart(file_body);

        // 5.3:創建主體內容資源存儲對象
        MimeBodyPart content = new MimeBodyPart();
     // 把主體內容混合到資源存儲對象里
     mixed.addBodyPart(content);
// 5.4:設置多資源內容 //=============== 構建郵件內容:多信息片段郵件 使用Content-Type:multipart/related ===============// // 5.4.1:構建一個多資源的郵件塊 用來把 文本內容資源 和 圖片資源合並;;;related代表多資源關聯 MimeMultipart text_img_related = new MimeMultipart("related"); //text_img_related.setSubType("related"); //注:這里為什么填寫related的請去查閱Multipart類型或者去文章開頭跳轉我之前上一篇郵件介紹 // 5.4.2:把關聯的把多資源郵件塊 混合到mixed多資源郵件模塊里 content.setContent(text_img_related); // 5.4.3:創建圖片資源 MimeBodyPart img_body = new MimeBodyPart(); DataHandler dhImg = new DataHandler(JavaxJavaMailClient.class.getResource("static/b.png")); img_body.setDataHandler(dhImg); //設置dhImg圖片處理 img_body.setContentID("imgA"); //設置資源圖片名稱ID // 5.4.4:創建文本資源,文本資源並引用上面的圖片ID(因為這兩個資源我做了關聯) MimeBodyPart text_body = new MimeBodyPart(); text_body.setContent("<img src='cid:imgA' width=100/> 我是螞蟻小哥!!","text/html;charset=UTF-8"); // 5.4.5:把創建出來的兩個資源合並到多資源模塊了 text_img_related.addBodyPart(img_body); text_img_related.addBodyPart(text_body); //===========================================================================================// // 6:設置我們處理好的資源(存放到Message) message.setContent(mixed); // 7:保存上面設置的郵件內容 message.saveChanges(); // 8:獲取Transport對象 Transport transport = session.getTransport(); //9:smtp驗證,就是你用來發郵件的郵箱用戶名密碼(若在之前的properties中指定默認值,這里可以不用再次設置) transport.connect(null, null, authCode); //10:發送郵件 transport.sendMessage(message, message.getAllRecipients()); // 發送 } }

5:使用JavaMail和Thymeleaf模板發送HTML內嵌圖片格式

  下面我將使用JavaMail通過Thymeleaf模板的方式發送HTML內容(HTML內容存在資源圖片關聯)+附件內容,其實這是方式都是通過2~4小節的慢慢改造,學會這種方式,那么我們發送公司業務上的一些郵件是沒有太大壓力的;下面我不在展示是全部代碼,只把補充的代碼展示出來,其它的參照第4節代碼

      <!--導入thymeleaf坐標-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf</artifactId>
            <version>3.0.12.RELEASE</version>
        </dependency>
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {padding: 0;margin: 0;}
        h5 {width: 300px;height: 40px;margin: 10px auto;
            text-align: center;font: normal 500 14px/40px '微軟雅黑';
            color: rgba(0, 0, 0, .8);border: 1px dashed #1c8b9e;
            border-radius: 5px;box-shadow: 10px 10px 30px 2px #f00;}
        img {width: 300px;height: 40px;}
        div {text-align: center;border: 1px solid #f00;margin: auto;}
    </style>
</head>
<body>
<h5>感謝<span th:text="${name}"></span>同志對我們的肯定和支持!</h5>
<!--注意這個資源一定要引用郵件發送關聯的圖片資源-->
<div><img src="cid:imgA" alt=""></div>
</body>
</html>
emailTemplate.html模板代碼,就以此模板發送資源
 /***
     * 模板解析方法,解析出一個String的html返回 
     * @return
     */
    public String templateHtml(){
        //設置類加載模板處理器
        ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver();
        //設置前綴后綴
        resolver.setPrefix("/static/");
        resolver.setSuffix(".html");
        //創建模板引擎處理器
        TemplateEngine engine = new TemplateEngine();
        //設置引擎使用的模板文件
        engine.setTemplateResolver(resolver);
        //創建Context來為模板設置填充數據
        Context context = new Context();
        //填充模板里的數據
        context.setVariable("name","螞蟻小哥");
        //具體處理,把模板和數據合並成一個新的文本
        //注:文件我直接放在resources/templates文件根目錄下,如果有多層目錄,需要寫明文件位置(或者設置過前綴和后綴)
        return engine.process("emailTemplate", context);
    }

6:使用JavaMail接收郵件(未解析)

  為了可以更好的測試郵件的接收,我這里使用163郵箱,把之前的郵件全部刪除了,用QQ有限發送了兩封郵件給163郵箱,下面這個只是其中一封郵件內容

public class JavaxJavaMailClient {

    public String emailHost = "pop.163.com";        //接收郵件的主機
    public String storeType = "pop3";               //郵件接收的協議
    public String fromEmail = "antladdie@163.com";  //接收郵件的郵箱
    public String authCode = "QWETREWWEWERTWWW";    //接收郵件的郵箱授權碼

    @Test
    public void ClientTestD() throws MessagingException, IOException {
        // 1:初始化默認參數
        Properties props = new Properties();
        props.setProperty("mail.host", emailHost);
        props.setProperty("mail.store.protocol", storeType);
        props.setProperty("mail.user", fromEmail);

        // 2:獲取連接
        Session session = Session.getInstance(props);
        session.setDebug(false);

        // 3:獲取Store對象
        Store store = session.getStore();
        store.connect(null, authCode);   //POP3服務器登錄認證,user我們在properties中已經指定默認

        // 4:獲取收件箱內容:(電子郵件)收件箱  folder:郵件夾
        Folder folder = store.getFolder("INBOX");
        // 設置對郵件帳戶的訪問權限
        // Folder.READ_ONLY (只讀或者1)        Folder.READ_WRITE(只寫或者2)
        folder.open(Folder.READ_WRITE);

        // 5:得到郵箱帳戶中的所有郵件
        Message[] messages = folder.getMessages();

        //循環遍歷郵件
        for (Message message : messages) {
            String subject = message.getSubject();  // 獲得郵件主題
            Address from = message.getFrom()[0];    // 獲得發送者地址
            System.out.println("郵件的主題為: " + subject + "發件人地址為: " + from);
            System.out.println("郵件的內容為:");
            // 輸出郵件的全部內容到控制台
            message.writeTo(System.out);
        }
        // 關閉郵件夾對象
        folder.close(false);
        store.close(); // 關閉連接對象
    }
}

補:打印出的第一條郵件內容:

郵件的主題為: 測試接收郵件(無附件)①    發件人地址為: =?gb18030?B?sai/vLTL0KO1xLH4?= <xiaofeng6699@vip.qq.com>
郵件的內容為:
Received: from xmbg7.mail.qq.com (unknown [101.91.43.54])
    by mx47 (Coremail) with SMTP id YcCowAAHhSLsVZxhew5GCw--.44623S3;
    Tue, 23 Nov 2021 10:46:04 +0800 (CST)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vip.qq.com;
    s=s201512; t=1637635564;
    bh=hx6m189GKLvHwYK7EPgjUY6W63aDJzkYLaZobsCH5fw=;
    h=From:To:Subject:Date;
    b=enOyaq4hkcY0sVHuLe3O6f0wz9gctOI9knFJ438sldmBa4gFpX7I6Ucv9Npe0BtTz
     +89KfHw6wUpfiBmgo119MRTkR8m1gE8BXyFCUgdq2qaYHPkf0sNAaZDtDGan7rRQuG
     oQmP+mXeqP3/KHtJNAMYEAJQF03qYipFKcPXA964=
X-QQ-FEAT: zaIfg0hwV2qA1LHh5sNQ3yDC93UmKYlF+NlagoYC+rE=
X-QQ-SSF: 000100000000004000000000000000Z
X-QQ-XMAILINFO: NBrlLnjHQiaZ3bWTg2+kaq2WcXqqqVbSme9UoRGcm/EacxcEzzBQa78nxQndrm
     eF/UAg21TqvTk30iPgxBkFSCSf9NIRTG3wpo2TUtMU4b/2LEU+Be3z3i/hOQaoe09Y2PYIfBfkona
     FEzXSl8LNQHYStRGXPoD/TwYeiQfXWnMIOnEU3EgJpJ5uznkMMFN6wldar9WyhOYPE3h2nLLSuSN4
     RT4Prw9m7uF/IJoSE5jMEI2hO3EE7YetoenF7EULwI+s5KyKyH9Y+hMDbVsZv5mnQ+aBLJJSsBJba
     Rb4Z7Di6PZfKS9MN2IQvcKNzNlgQ5mIhSiwLIlukwMz5W+XnOVp7gLIn4HXK5b4WmqclfbrHV0b1q
     SM/HdMWzjT29cV1ZM7A2+uiCPFR9Tf1V0EDOhJGN+GqCgV4Z8ETjrWz47C4E3B993M+HiNQj1PY03
     UhD+8DsrsGqZqVTKoRP3SQKrZSygkyrYpBkevCoRex26JH67dHQxRXUGgCci1UZ8IA9/kMa28JQ+h
     fM0Cly0ZQtW32QAJSPUcHdrhxkfWQo3mfeVHg6NEp18arwXhEPp4Io8PTcmCGq6G0k5ZsjMVXaUtN
     DRUFEVQVo2jxint/dyQoGoxsNVkUdqYijW+VTMSdMkC4wnuEqjZ5a+8aiD1uMGm/eEkwW2qF4tZi7
     XZ5MKHKvvze1HSPsol0cuP6og8hbc490IH1Lz4Slz/HSTWL95IwkqvniKJtVKldTpa+0GYEY1dPIx
     KAmwahttxUgWv39mZ4VJgxL5Ozb+IX4tZ4JyTsjx4sk8RCHkEQVHsXZn+nSrQwPvlmMlaQ5sp/N7W
     Jt7yf2TpaHTWhtTek3P08MNRmkPqMgJSKCrn+3t/TJq8HdAKQi2NY0B0Mnseh5S7pWEu3i5xh1Rh
X-HAS-ATTACH: no
X-QQ-BUSINESS-ORIGIN: 2
X-Originating-IP: 122.97.149.60
X-QQ-STYLE: X-QQ-mid: webmail623t1637635564t3630643
From: "=?gb18030?B?sai/vLTL0KO1xLH4?=" <xiaofeng6699@vip.qq.com>
To: "=?gb18030?B?YW50bGFkZGll?=" <antladdie@163.com>
Subject: =?gb18030?B?suLK1L3TytXTyrz+KM7euL28/imi2Q==?=
Mime-Version: 1.0
Content-Type: multipart/alternative;
    boundary="----=_NextPart_619C55EB_107CACC0_425E3EAC"
Content-Transfer-Encoding: 8Bit
Date: Tue, 23 Nov 2021 10:46:03 +0800
X-Priority: 3
Message-ID: <tencent_269D4D83D43DB8D6F68434FA99562BC26708@qq.com>
X-QQ-MIME: TCMime 1.0 by Tencent
X-Mailer: QQMail 2.x
X-QQ-Mailer: QQMail 2.x
X-CM-TRANSID:YcCowAAHhSLsVZxhew5GCw--.44623S3
Authentication-Results: mx47; spf=pass smtp.mail=xiaofeng6699@vip.qq.c
    om; dkim=pass header.i=@vip.qq.com
X-Coremail-Antispam: 1Uf129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7v73
    VFW2AGmfu7bjvjm3AaLaJ3UbIYCTnIWIevJa73UjIFyTuYvjxU4vPfDUUUU

This is a multi-part message in MIME format.

------=_NextPart_619C55EB_107CACC0_425E3EAC
Content-Type: text/plain;
    charset="gb18030"
Content-Transfer-Encoding: base64

SSdtIGdvaW5nIHRvIGRvIGEgbWFpbGJveCBhY2NlcHRhbmNlIHRlc3QuztLSqtf20ru49tPK
z+S908rVsuLK1A==

------=_NextPart_619C55EB_107CACC0_425E3EAC
Content-Type: text/html;
    charset="gb18030"
Content-Transfer-Encoding: base64

PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNo
YXJzZXQ9R0IxODAzMCI+PHNwYW4gc3R5bGU9ImZvbnQtZmFtaWx5OiBQaW5nRmFuZ1NDLVJl
Z3VsYXIsICZxdW90O01pY3Jvc29mdCBZYWhlaSZxdW90OywgJnF1b3Q7XFw1RkFFyO3Rxbra
JnF1b3Q7LCBzYW5zLXNlcmlmOyBmb250LXNpemU6IDE2cHg7Ij5JJ20gZ29pbmcgdG8gZG8g
YSBtYWlsYm94IGFjY2VwdGFuY2UgdGVzdC48L3NwYW4+PGRpdj48Zm9udCBmYWNlPSJQaW5n
RmFuZ1NDLVJlZ3VsYXIsIE1pY3Jvc29mdCBZYWhlaSwgXFw1RkFFyO3RxbraLCBzYW5zLXNl
cmlmIj48c3BhbiBzdHlsZT0iZm9udC1zaXplOiAxNnB4OyI+ztLSqtf20ru49tPKz+S908rV
suLK1Dwvc3Bhbj48L2ZvbnQ+PC9kaXY+

------=_NextPart_619C55EB_107CACC0_425E3EAC--

7:使用JavaMail接收郵件(解析郵件內容)

import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author AnHui_XiaoYang
 * @Email 939209948@qq.com
 * @Date 2021/11/24 13:52
 * @Description 解析郵箱服務器返回的郵件
 */
public class MailParsingTool {

    /***
     * 獲取郵箱的基本郵件信息
     * @param folder   收件箱對象
     * @return 返回郵箱基本信息
     */
    public static Map<String, Integer> emailInfo(Folder folder) throws MessagingException {
        Map<String, Integer> emailInfo = new HashMap<>();
        // 由於POP3協議無法獲知郵件的狀態,所以getUnreadMessageCount得到的是收件箱的郵件總數
        emailInfo.put("unreadMessageCount", folder.getUnreadMessageCount());        //未讀郵件數
        // 由於POP3協議無法獲知郵件的狀態,所以下面(刪除、新郵件)得到的結果始終都是為0
        emailInfo.put("deletedMessageCount", folder.getDeletedMessageCount());      //刪除郵件數
        emailInfo.put("newMessageCount", folder.getNewMessageCount());              //新郵件
        // 獲得收件箱中的郵件總數
        emailInfo.put("messageCount", folder.getMessageCount());                    //郵件總數
        return emailInfo;
    }

    /***
     * 獲得郵件主題
     * @param msg 郵件內容
     * @return 解碼后的郵件主題
     * @throws UnsupportedEncodingException
     * @throws MessagingException
     */
    public static String getSubject(MimeMessage msg) throws UnsupportedEncodingException, MessagingException {
        return decodeText(msg.getSubject());
    }

    /***
     * 獲得郵件發件人
     * @param msg 郵件內容
     * @return 姓名 <Email地址>
     * @throws MessagingException
     * @throws UnsupportedEncodingException
     */
    public static String getFrom(MimeMessage msg) throws MessagingException, UnsupportedEncodingException {
        String from = "";
        Address[] froms = msg.getFrom();
        if (froms.length < 1) {
            throw new MessagingException("沒有發件人!");
        }

        InternetAddress address = (InternetAddress) froms[0];
        String person = address.getPersonal();
        if (person != null) {
            person = decodeText(person) + " ";
        } else {
            person = "";
        }
        from = person + "<" + address.getAddress() + ">";
        return from;
    }

    /***
     * 根據收件人類型,獲取郵件收件人、抄送和密送地址。如果收件人類型為空,則獲得所有的收件人
     * type可選值
     * <p>Message.RecipientType.TO  收件人</p>
     * <p>Message.RecipientType.CC  抄送</p>
     * <p>Message.RecipientType.BCC 密送</p>
     * @param msg  郵件內容
     * @param type 收件人類型
     * @return 收件人1 <郵件地址1>, 收件人2 <郵件地址2>, ...
     * @throws MessagingException
     */
    public static String getReceiveAddress(MimeMessage msg, Message.RecipientType type) throws MessagingException {
        StringBuilder recipientAddress = new StringBuilder();
        Address[] addresss = null;
        if (type == null) {
            addresss = msg.getAllRecipients();
        } else {
            addresss = msg.getRecipients(type);
        }

        if (addresss == null || addresss.length < 1) {
            if (type == null) {
                throw new MessagingException("沒有收件人!");
            } else if ("Cc".equals(type.toString())) {
                throw new MessagingException("沒有抄送人!");
            } else if ("Bcc".equals(type.toString())) {
                throw new MessagingException("沒有密送人!");
            }
        }

        for (Address address : addresss) {
            InternetAddress internetAddress = (InternetAddress) address;
            recipientAddress.append(internetAddress.toUnicodeString()).append(",");
        }
        //刪除最后一個逗號
        recipientAddress.deleteCharAt(recipientAddress.length() - 1);
        return recipientAddress.toString();
    }

    /***
     * 獲得郵件發送時間
     * @param msg 郵件內容
     * @return 默認返回:yyyy年mm月dd日 星期X HH:mm
     * @throws MessagingException
     */
    public static String getSentDate(MimeMessage msg, String pattern) throws MessagingException {
        Date receivedDate = msg.getSentDate();
        if (receivedDate == null)
            return "";
        if (pattern == null || "".equals(pattern))
            pattern = "yyyy年MM月dd日 E HH:mm ";
        return new SimpleDateFormat(pattern).format(receivedDate);
    }

    /***
     * 判斷郵件是否已讀  www.2cto.com
     * @param msg 郵件內容
     * @return 如果郵件已讀返回true, 否則返回false
     * @throws MessagingException
     */
    public static boolean isSeen(MimeMessage msg) throws MessagingException {
        return msg.getFlags().contains(Flags.Flag.SEEN);
    }

    /***
     * 判斷郵件是否需要閱讀回執
     * @param msg 郵件內容
     * @return 需要回執返回true, 否則返回false
     * @throws MessagingException
     */
    public static boolean isReplySign(MimeMessage msg) throws MessagingException {
        boolean replySign = false;
        String[] headers = msg.getHeader("Disposition-Notification-To");
        if (headers != null)
            replySign = true;
        return replySign;
    }

    /***
     * 獲得郵件的優先級
     * @param msg 郵件內容
     * @return 1(High):緊急  3:普通(Normal)  5:低(Low)
     * @throws MessagingException
     */
    public static String getPriority(MimeMessage msg) throws MessagingException {
        String priority = "普通";
        String[] headers = msg.getHeader("X-Priority");
        if (headers != null) {
            String headerPriority = headers[0];
            if (headerPriority.contains("1") || headerPriority.contains("High"))
                priority = "緊急";
            else if (headerPriority.contains("5") || headerPriority.contains("Low"))
                priority = "低";
            else
                priority = "普通";
        }
        return priority;
    }

    /***
     * 獲得郵件文本內容
     * @param part    郵件體
     * @param content 存儲郵件文本內容的字符串
     * @throws MessagingException
     * @throws IOException
     */
    public static void getMailTextContent(Part part, StringBuffer content) throws MessagingException, IOException {
        //如果是文本類型的附件,通過getContent方法可以取到文本內容,但這不是我們需要的結果,所以在這里要做判斷
        boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;
        if (part.isMimeType("text/*") && !isContainTextAttach) {
            content.append(part.getContent().toString());
        } else if (part.isMimeType("message/rfc822")) {
            getMailTextContent((Part) part.getContent(), content);
        } else if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                getMailTextContent(bodyPart, content);
            }
        }
    }

    /***
     * 文本解碼
     * @param encodeText 解碼MimeUtility.encodeText(String text)方法編碼后的文本
     * @return 解碼后的文本
     * @throws UnsupportedEncodingException
     */
    public static String decodeText(String encodeText) throws UnsupportedEncodingException {
        if (encodeText == null || "".equals(encodeText)) {
            return "";
        } else {
            return MimeUtility.decodeText(encodeText);
        }
    }

    /***
     * 判斷郵件中是否包含附件 (Part為Message接口)
     * @param part 郵件內容
     * @return 郵件中存在附件返回true,不存在返回false
     * @throws MessagingException
     * @throws IOException
     */
    public static boolean isContainAttachment(Part part) throws MessagingException, IOException {
        boolean flag = false;
        if (part.isMimeType("multipart/*")) {
            MimeMultipart multipart = (MimeMultipart) part.getContent();
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                BodyPart bodyPart = multipart.getBodyPart(i);
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    flag = true;
                } else if (bodyPart.isMimeType("multipart/*")) {
                    flag = isContainAttachment(bodyPart);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.contains("application")) {
                        flag = true;
                    }
                    if (contentType.contains("name")) {
                        flag = true;
                    }
                }
                if (flag) break;
            }
        } else if (part.isMimeType("message/rfc822")) {
            flag = isContainAttachment((Part) part.getContent());
        }
        return flag;
    }

    /***
     * 保存附件
     * @param part    郵件中多個組合體中的其中一個組合體
     * @param destDir 附件保存目錄
     * @throws UnsupportedEncodingException
     * @throws MessagingException
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void saveAttachment(Part part, String destDir) throws UnsupportedEncodingException, MessagingException,
            FileNotFoundException, IOException {
        if (part.isMimeType("multipart/*")) {
            Multipart multipart = (Multipart) part.getContent();    //復雜體郵件
            //復雜體郵件包含多個郵件體
            int partCount = multipart.getCount();
            for (int i = 0; i < partCount; i++) {
                //獲得復雜體郵件中其中一個郵件體
                BodyPart bodyPart = multipart.getBodyPart(i);
                //某一個郵件體也有可能是由多個郵件體組成的復雜體
                String disp = bodyPart.getDisposition();
                if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
                    InputStream is = bodyPart.getInputStream();
                    saveFile(is, destDir, decodeText(bodyPart.getFileName()));
                } else if (bodyPart.isMimeType("multipart/*")) {
                    saveAttachment(bodyPart, destDir);
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.contains("name") || contentType.contains("application")) {
                        saveFile(bodyPart.getInputStream(), destDir, decodeText(bodyPart.getFileName()));
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            saveAttachment((Part) part.getContent(), destDir);
        }
    }

    /***
     * 讀取輸入流中的數據保存至指定目錄 
     * @param is       輸入流
     * @param fileName 文件名
     * @param destDir  文件存儲目錄
     * @throws FileNotFoundException
     * @throws IOException
     */
    private static void saveFile(InputStream is, String destDir, String fileName)
            throws FileNotFoundException, IOException {
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream(destDir + fileName));
        int len = -1;
        while ((len = bis.read()) != -1) {
            bos.write(len);
            bos.flush();
        }
        bos.close();
        bis.close();
    }
}
郵件解析工具類 MailParsingTool
public class JavaxJavaMailClient {

    public String emailHost = "pop.163.com";        //接收郵件的主機
    public String storeType = "pop3";               //郵件接收的協議
    public String fromEmail = "antladdie@163.com";  //接收郵件的郵箱
    public String authCode = "NUOVRPTNUOJIEIYJ";    //接收郵件的郵箱授權碼

    @Test
    public void ClientTestD() throws MessagingException, IOException {
        // 1:初始化默認參數
        Properties props = new Properties();
        props.setProperty("mail.host", emailHost);
        props.setProperty("mail.store.protocol", storeType);
        props.setProperty("mail.user", fromEmail);

        // 2:獲取連接
        Session session = Session.getInstance(props);
        session.setDebug(false);

        // 3:獲取Store對象
        Store store = session.getStore();
        store.connect(null, authCode);   //POP3服務器登錄認證,user我們在properties中已經指定默認

        // 4:獲取收件箱內容:(電子郵件)收件箱  folder:郵件夾
        Folder folder = store.getFolder("INBOX");
        // 設置對郵件帳戶的訪問權限
        // Folder.READ_ONLY (只讀或者1)        Folder.READ_WRITE(只寫或者2)
        folder.open(Folder.READ_WRITE);

        //獲取郵箱基本信息
        Map<String, Integer> map = MailParsingTool.emailInfo(folder);
        System.out.println(map);

        // 得到收件箱中的所有郵件,並解析
        Message[] messages = folder.getMessages();
        parseMessage(messages);
        
        // 關閉郵件夾對象
        folder.close(false);
        store.close(); // 關閉連接對象
    }

    public static void parseMessage(Message... messages) throws MessagingException, IOException {
        //判斷郵件是否為空
        if (messages == null || messages.length < 1) {
            throw new MessagingException("未找到要解析的郵件!");
        }

        // 解析所有郵件
        for (int i = 0, count = messages.length; i < count; i++) {
            MimeMessage msg = (MimeMessage) messages[i];
            System.out.println("-----------解析第" + msg.getMessageNumber() + "封郵件---------------");
            System.out.println("主題: " + MailParsingTool.getSubject(msg));
            System.out.println("發件人: " + MailParsingTool.getFrom(msg));
            System.out.println("收件人:" + MailParsingTool.getReceiveAddress(msg, Message.RecipientType.TO));
            System.out.println("發送時間:" + MailParsingTool.getSentDate(msg, null));
            System.out.println("是否已讀:" + MailParsingTool.isSeen(msg));
            System.out.println("郵件優先級:" + MailParsingTool.getPriority(msg));
            System.out.println("是否需要回執:" + MailParsingTool.isReplySign(msg));
            System.out.println("郵件大小:" + msg.getSize() * 1024 + "kb");
            boolean isContainerAttachment = MailParsingTool.isContainAttachment(msg);
            System.out.println("是否包含附件:" + isContainerAttachment);
            if (isContainerAttachment) {
                //獲取文件的存儲目錄
                String path = JavaxJavaMailClient.class.getClassLoader().getResource("").getPath();
                //獲取文件的前綴
                String strFile = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
                MailParsingTool.saveAttachment(msg, path + strFile + "_"); //保存附件
            }
            //用來存儲正文的對象
            StringBuffer content = new StringBuffer();
            //處理郵件正文
            MailParsingTool.getMailTextContent(msg, content);
            System.out.println("郵件正文:" + content);
            System.out.println("-----------第" + msg.getMessageNumber() + "封郵件解析結束------------");
            System.out.println();
        }
    }
}

8:結尾

  不知不覺中我們就會使用JavaMail發送和接收郵件,不過我要告訴大家的是javax.JavaMail最后一個版本發布於2018年8月,此后則停止更新;如果在新項目中使用到了郵件發送我推薦大家使用Jakarta Mail,它是JavaMail的前身;其實Jakarta Mail使用起來和JavaMail基本上一樣,把上面代碼拷貝過去照樣運行,不過我下一篇主要介紹SpringBoot集成JakartaMail


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM