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

posted @ 2021-11-29 09:22  蚂蚁小哥  阅读(26240)  评论(1编辑  收藏  举报