欢迎光临
我们一直在努力

Java邮件编程全指南:从入门到精通

这是一份非常详细、实用、通俗易懂、权威且全面的 Java 邮件编程指南。希望这篇文章能对你有帮助,关注我,你的肯定就是我输出的最大动力!


目录

  • 邮件基础与协议

    • 1.1 邮件是如何工作的?
    • 1.2 SMTP (简单邮件传输协议)
    • 1.3 POP3 (邮局协议第3版)
    • 1.4 IMAP (互联网消息访问协议)
    • 1.5 SMTP vs POP3 vs IMAP
    • 1.6 MIME (多用途互联网邮件扩展)
  • JavaMail API 概览

    • 2.1 JavaMail 是什么?
    • 2.2 核心组件介绍
      • Session
      • Message
      • Transport (发送)
      • Store (接收)
      • Folder
    • 2.3 依赖引入 (Maven / Gradle / JARs)
  • 配置与发送邮件 (SMTP)

    • 3.1 设置 Session 属性
      • 3.1.1 基本属性 (mail.smtp.host, mail.smtp.port)
      • 3.1.2 身份验证 (mail.smtp.auth)
      • 3.1.3 传输层安全 (mail.smtp.starttls.enable, mail.smtp.ssl.enable)
      • 3.1.4 调试 (mail.debug)
    • 3.2 创建邮件消息 (Message)
      • 3.2.1 设置发件人、收件人、抄送、密送 (From, To, Cc, Bcc)
      • 3.2.2 设置主题 (Subject)
      • 3.2.3 设置正文 (setText / setContent)
      • 3.2.4 设置日期 (setSentDate)
      • 3.2.5 设置邮件头 (自定义头信息)
    • 3.3 发送邮件 (Transport)
    • 3.4 发送纯文本邮件 (完整示例代码)
    • 3.5 发送 HTML 邮件
    • 3.6 发送带附件的邮件 (Multipart, MimeBodyPart)
    • 3.7 发送带内嵌图片的 HTML 邮件 (Content-ID)
    • 3.8 常见 SMTP 服务器配置示例 (Gmail, QQ, 163, 企业邮箱)
  • 接收邮件 (POP3 / IMAP)

    • 4.1 设置 Session 属性 (接收侧)
      • 4.1.1 POP3 (mail.pop3.host, mail.pop3.port, mail.pop3.starttls.enable, mail.pop3.ssl.enable)
      • 4.1.2 IMAP (mail.imap.host, mail.imap.port, mail.imap.starttls.enable, mail.imap.ssl.enable)
    • 4.2 连接到邮件存储 (Store)
    • 4.3 打开邮箱文件夹 (Folder)
      • 4.3.1 常见文件夹 (INBOX, Sent, Drafts, Trash)
      • 4.3.2 文件夹访问模式 (READ_ONLY, READ_WRITE)
    • 4.4 获取邮件列表
    • 4.5 解析邮件内容 (Message)
      • 4.5.1 获取发件人、收件人、主题、日期
      • 4.5.2 获取纯文本正文 (getContent / getInputStream)
      • 4.5.3 获取 HTML 正文
      • 4.5.4 获取附件 (Multipart, BodyPart, getContentType, getDisposition, getInputStream, saveFile)
    • 4.6 使用 POP3 接收邮件 (完整示例代码)
    • 4.7 使用 IMAP 接收邮件 (完整示例代码)
    • 4.8 搜索邮件 (Folder.search(SearchTerm term))
    • 4.9 删除邮件 (message.setFlag(Flags.Flag.DELETED, true), folder.expunge())
    • 4.10 邮件回复
    • 4.11 邮件抄送与密送
    • 1.12 邮件转发
    • 4.13 邮件附件下载
    • 4.14 邮件标记已读与未读
    • 4.15 邮件回执
  • 处理复杂邮件结构与 MIME

    • 5.1 Multipart 深入理解
    • 5.2 MimeBodyPart 详解
    • 5.3 内容类型 (Content-Type) 详解 (text/plain, text/html, image/jpeg, application/pdf, multipart/mixed, multipart/alternative, multipart/related)
    • 5.4 内容传输编码 (Content-Transfer-Encoding) (7bit, 8bit, base64, quoted-printable)
    • 5.5 内容处置 (Content-Disposition) (inline, attachment)
    • 5.6 处理嵌套的 Multipart 结构
  • 安全性与最佳实践

    • 6.1 SSL/TLS 加密的重要性
    • 6.2 安全地存储密码 (避免硬编码)
    • 6.3 处理敏感信息
    • 6.4 防范邮件注入攻击
    • 6.5 连接池与资源管理 (Transport 和 Store 的关闭)
    • 6.6 错误处理与重试机制
    • 6.7 日志记录
  • 常见问题与故障排除

    • 7.1 认证失败 (AuthenticationFailedException)
    • 7.2 连接超时 (ConnectException)
    • 7.3 SSL/TLS 证书问题 (SSLHandshakeException)
    • 7.4 端口被阻止 (防火墙)
    • 7.5 附件无法下载或显示
    • 7.6 HTML 邮件样式丢失
    • 7.7 中文乱码问题 (字符集设置)
  • 完整系统案例

    • 案例一:邮件发送工具类 (支持文本、HTML、附件、图片)
    • 案例二:邮件接收监控服务 (IMAP IDLE/Polling)
    • 案例三:简单的邮件客户端 (命令行或 Swing)
    • 案例四:Spring Boot 集成邮件发送

  • 1. 邮件基础与协议

    要理解 Java 邮件编程,首先要明白电子邮件系统是如何工作的。它依赖于一系列标准协议。

    • 1.1 邮件是如何工作的?

      • 当你发送邮件时,你的邮件客户端 (如 Outlook, Thunderbird) 或应用程序使用 SMTP 协议将邮件发送到你的邮件服务器 (SMTP Server)。
      • 你的邮件服务器再将邮件通过 SMTP 协议转发给收件人的邮件服务器。
      • 收件人使用邮件客户端或应用程序,通过 POP3 或 IMAP 协议连接到他们的邮件服务器来下载或查看邮件。
      • MIME 定义了邮件内容的格式,如文本、HTML、图片、附件等如何编码和在邮件中表示。
    • 1.2 SMTP (简单邮件传输协议)

      • 作用: 负责将邮件从发送方传输到接收方的邮件服务器。只负责发送。
      • 端口: 默认端口 25 (非加密)。安全端口 465 (SSL) 或 587 (STARTTLS)。
      • 过程: 建立连接 -> 身份验证 (可选) -> 发送邮件信息 (MAIL FROM, RCPT TO, DATA) -> 断开连接。
      • Java 对应: Transport 类。
    • 1.3 POP3 (邮局协议第3版)

      • 作用: 允许邮件客户端从邮件服务器下载邮件。通常下载后会从服务器删除邮件 (可配置)。
      • 端口: 默认端口 110 (非加密)。安全端口 995 (SSL)。
      • 特点: 简单,离线访问为主。邮件通常存储在本地。不支持多文件夹同步。
      • Java 对应: Store 类 (连接 POP3 服务器)。
    • 1.4 IMAP (互联网消息访问协议)

      • 作用: 允许邮件客户端访问邮件服务器上的邮件。邮件保留在服务器上。支持同步多个客户端的状态、文件夹管理、邮件搜索等。
      • 端口: 默认端口 143 (非加密)。安全端口 993 (SSL)。
      • 特点: 更强大,支持在线和离线操作。邮件状态 (已读/未读、星标等) 在服务器同步。
      • Java 对应: Store 类 (连接 IMAP 服务器)。
    • 1.5 SMTP vs POP3 vs IMAP

      协议方向主要功能邮件存储位置特点
      SMTP 发送 发送邮件 发送端使用
      POP3 接收 下载邮件 本地 简单,离线为主
      IMAP 接收 访问和同步邮件 服务器 功能强大,支持同步
    • 1.6 MIME (多用途互联网邮件扩展)

      • 作用: 扩展了原始只能发送纯文本的邮件协议 (RFC 822),允许在邮件中包含:
        • 非文本内容 (图片、音频、视频、应用程序文件等)。
        • 多语言字符集 (解决乱码)。
        • 富文本格式 (HTML)。
        • 组合内容 (正文+附件,正文+内嵌图片)。
      • 核心概念:
        • Content-Type: 定义内容的类型和子类型 (如 text/plain, text/html, image/jpeg, multipart/mixed)。
        • Content-Transfer-Encoding: 定义内容如何编码以便安全传输 (如 7bit, 8bit, base64, quoted-printable)。
        • Content-Disposition: 指示内容如何显示 (inline – 内嵌在邮件中显示, attachment – 作为附件下载)。
        • Multipart: MIME 消息可以包含多个部分 (BodyPart),每个部分有自己的头信息和内容。multipart/mixed 用于混合独立部分 (如正文+附件),multipart/alternative 用于同一内容的不同版本 (如纯文本+HTML),multipart/related 用于相关资源 (如 HTML + 内嵌图片)。
      • Java 对应: MimeMessage, MimeMultipart, MimeBodyPart 类。

    2. JavaMail API 概览

    • 2.1 JavaMail 是什么? JavaMail API 是 Java 平台的一个标准扩展 (JSR 919),提供了一套独立于协议的框架和抽象,用于构建邮件和消息应用程序。它封装了 SMTP、POP3、IMAP 等协议的底层细节,开发者可以使用统一的 API 来发送和接收邮件。

    • 2.2 核心组件介绍

      • Session (会话):
        • 邮件操作的核心配置点。
        • 包含连接邮件服务器所需的所有属性 (主机名、端口、用户名、密码、协议、加密设置等)。
        • 通过 Properties 对象或 Authenticator 对象创建。
        • Session session = Session.getInstance(properties, authenticator);
      • Message (消息):
        • 表示一封邮件。
        • 通常是 MimeMessage (支持 MIME 格式)。
        • 包含发件人、收件人、主题、正文、日期、邮件头等信息。
        • Message message = new MimeMessage(session);
      • Transport (传输):
        • 用于发送邮件。
        • 与 SMTP 服务器通信。
        • 通过 Session 获得:Transport transport = session.getTransport("smtp");
        • 使用方法:transport.connect(); -> transport.sendMessage(message, message.getAllRecipients()); -> transport.close();
      • Store (存储):
        • 用于访问邮件存储 (邮箱)。
        • 与 POP3 或 IMAP 服务器通信。
        • 通过 Session 获得:Store store = session.getStore("imap"); 或 Store store = session.getStore("pop3");
        • 使用方法:store.connect(username, password); -> 打开 Folder -> 操作邮件 -> 关闭 Folder -> store.close();
      • Folder (文件夹):
        • 代表邮箱中的一个文件夹 (如 INBOX, Sent Mail)。
        • 通过 Store 获得:Folder folder = store.getFolder("INBOX");
        • 使用方法:folder.open(Folder.READ_ONLY); 或 folder.open(Folder.READ_WRITE); -> 获取邮件 (Message[] messages = folder.getMessages();) -> 操作邮件 -> folder.close(false); (false 表示不删除标记为 DELETED 的邮件) 或 folder.close(true); (true 表示删除标记为 DELETED 的邮件)。
    • 2.3 依赖引入 JavaMail API 不是标准 Java SE 的一部分,需要额外引入。

      • 官方 JARs: 从 JavaMail Project 下载 javax.mail.jar。
      • Maven: <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>javax.mail</artifactId>
        <version>1.6.2</version> <!– 请检查最新版本 –>
        </dependency>
      • Gradle: implementation 'com.sun.mail:javax.mail:1.6.2' // 请检查最新版本
      • 另外,处理邮件内容 (如解析 HTML) 可能还需要引入其他库,如 javax.activation:activation (用于 MIME 类型处理和数据源),但在较新版本中可能已包含或使用其他实现。

    3. 配置与发送邮件 (SMTP)

    • 3.1 设置 Session 属性 创建一个 Properties 对象来配置 Session。

      Properties props = new Properties();

      • 3.1.1 基本属性: props.put("mail.smtp.host", "smtp.example.com"); // SMTP服务器地址
        props.put("mail.smtp.port", "587"); // SMTP服务器端口 (25, 465, 587)
      • 3.1.2 身份验证: (大多数服务器需要) props.put("mail.smtp.auth", "true"); // 启用认证
      • 3.1.3 传输层安全:
        • SSL: 直接使用 SSL 加密连接 (端口通常是 465) props.put("mail.smtp.ssl.enable", "true"); // 启用 SSL
          // 如果使用 SSL, 端口通常设置为 465
          props.put("mail.smtp.port", "465");
        • STARTTLS: 先建立非加密连接,然后升级到加密 (端口通常是 25 或 587) props.put("mail.smtp.starttls.enable", "true"); // 启用 STARTTLS
          // 端口通常为 587
          props.put("mail.smtp.port", "587");
      • 3.1.4 调试: (开发时很有用) props.put("mail.debug", "true"); // 启用调试输出

      创建带认证的 Session:

      Session session = Session.getInstance(props, new Authenticator() {
      @Override
      protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication("your_username@example.com", "your_password");
      }
      });

      或者使用更简洁的方式 (Java 8+):

      Session session = Session.getInstance(props, new javax.mail.Authenticator() {
      protected javax.mail.PasswordAuthentication getPasswordAuthentication() {
      return new javax.mail.PasswordAuthentication(username, password);
      }
      });

    • 3.2 创建邮件消息 (Message) 使用 MimeMessage 类。

      Message message = new MimeMessage(session);

      • 3.2.1 设置发件人、收件人、抄送、密送: // 设置发件人 (可以是字符串或 Address 对象)
        message.setFrom(new InternetAddress("sender@example.com"));

        // 设置主收件人 (To)
        message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("recipient1@example.com, recipient2@example.com"));

        // 设置抄送 (Cc)
        message.setRecipients(Message.RecipientType.CC, InternetAddress.parse("cc@example.com"));

        // 设置密送 (Bcc)
        message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse("bcc@example.com"));
        InternetAddress.parse 可以解析逗号分隔的地址列表。

      • 3.2.2 设置主题: message.setSubject("这是一封测试邮件主题");
      • 3.2.3 设置正文:
        • 纯文本:
        • HTML 内容 (稍后详细讲解): message.setContent("<h1>HTML 内容</h1><p>这是一封包含 <strong>HTML</strong> 格式的邮件。</p>", "text/html; charset=UTF-8");
      • 3.2.4 设置日期: (可选,默认是当前时间) message.setSentDate(new Date());
      • 3.2.5 设置邮件头: (自定义头信息,非必须) message.setHeader("X-Custom-Header", "MyValue");
    • 3.3 发送邮件 (Transport)

      try (Transport transport = session.getTransport()) {
      // 连接服务器 (如果 Session 属性中设置了用户名密码,这里通常不需要再提供)
      transport.connect(); // 或者 transport.connect(username, password); 如果 Session 未设置认证
      // 发送邮件
      transport.sendMessage(message, message.getAllRecipients());
      System.out.println("邮件发送成功!");
      } catch (MessagingException e) {
      e.printStackTrace(); // 处理发送异常
      }
      // 使用 try-with-resources 确保 Transport 自动关闭

    • 3.4 发送纯文本邮件 (完整示例代码)

      import java.util.Properties;
      import javax.mail.*;
      import javax.mail.internet.InternetAddress;
      import javax.mail.internet.MimeMessage;

      public class SimpleTextMailSender {

      public static void main(String[] args) {
      // 邮件服务器配置
      String host = "smtp.example.com"; // 替换为你的SMTP服务器
      int port = 587; // 常用端口 25(非SSL), 465(SSL), 587(STARTTLS)
      String username = "your_username@example.com"; // 替换为你的邮箱用户名
      String password = "your_password"; // 替换为你的邮箱密码/授权码
      String from = "sender@example.com"; // 发件人地址
      String to = "recipient@example.com"; // 收件人地址

      // 设置属性
      Properties props = new Properties();
      props.put("mail.smtp.host", host);
      props.put("mail.smtp.port", port);
      props.put("mail.smtp.auth", "true"); // 启用认证
      props.put("mail.smtp.starttls.enable", "true"); // 启用 STARTTLS (对于端口587)

      // 创建会话
      Session session = Session.getInstance(props, new Authenticator() {
      @Override
      protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(username, password);
      }
      });

      try {
      // 创建邮件
      Message message = new MimeMessage(session);
      message.setFrom(new InternetAddress(from));
      message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
      message.setSubject("JavaMail 纯文本测试邮件");
      message.setText("你好,\\n\\n这是一封使用 JavaMail API 发送的纯文本测试邮件。\\n\\n祝好!");

      // 发送邮件
      Transport.send(message); // 简化发送方式 (内部处理 Transport)

      System.out.println("纯文本邮件发送成功!");

      } catch (MessagingException e) {
      throw new RuntimeException("邮件发送失败", e);
      }
      }
      }

    • 3.5 发送 HTML 邮件 修改正文设置部分:

      String htmlContent = "<html><body>"
      + "<h1>HTML 邮件测试</h1>"
      + "<p>这是一封<strong>HTML格式</strong>的邮件。</p>"
      + "<p>可以包含 <span style='color: red;'>样式</span> 和 <a href='https://www.example.com'>链接</a>。</p>"
      + "</body></html>";
      message.setContent(htmlContent, "text/html; charset=UTF-8");

      注意:某些邮件客户端可能只显示纯文本版本或限制部分 HTML/CSS。确保提供良好的降级体验或同时提供纯文本版本 (见 Multipart/alternative)。

    • 3.6 发送带附件的邮件 需要使用 MimeMultipart 和 MimeBodyPart 来组合多个部分。

      import javax.mail.internet.MimeMultipart;
      import javax.mail.internet.MimeBodyPart;
      import javax.activation.DataHandler;
      import javax.activation.FileDataSource;
      import java.io.File;

      // … (Session 和 Message 创建同上)

      // 创建一个多部分消息
      MimeMultipart multipart = new MimeMultipart();

      // 创建文本正文部分
      MimeBodyPart textPart = new MimeBodyPart();
      textPart.setText("邮件正文内容,附件在下面。"); // 或使用 setContent 设置 HTML
      multipart.addBodyPart(textPart);

      // 创建附件部分
      MimeBodyPart attachmentPart = new MimeBodyPart();
      File file = new File("/path/to/your/file.pdf"); // 附件文件路径
      FileDataSource source = new FileDataSource(file);
      attachmentPart.setDataHandler(new DataHandler(source));
      attachmentPart.setFileName(file.getName()); // 设置附件显示的文件名
      multipart.addBodyPart(attachmentPart);

      // 将多部分内容设置为邮件内容
      message.setContent(multipart);

      // … (发送邮件同上)

    • 3.7 发送带内嵌图片的 HTML 邮件 原理:将图片作为 BodyPart 嵌入,并设置其 Content-ID,然后在 HTML 中使用 cid:ContentID 引用。

      // … (Session 和 Message 创建同上)

      // 创建一个多部分消息 (使用 'related' 类型表示内嵌资源相关)
      MimeMultipart multipart = new MimeMultipart("related");

      // 创建 HTML 正文部分 (父部分)
      MimeBodyPart htmlPart = new MimeBodyPart();

      // HTML 内容,引用图片的 cid
      String htmlContent = "<html><body>"
      + "<h1>包含图片的邮件</h1>"
      + "<p>图片内嵌在邮件中:</p>"
      + "<img src='cid:image1'>" // 注意 cid:image1
      + "</body></html>";
      htmlPart.setContent(htmlContent, "text/html; charset=UTF-8");
      multipart.addBodyPart(htmlPart);

      // 创建图片部分 (附件,但设置为内嵌)
      MimeBodyPart imagePart = new MimeBodyPart();
      File imageFile = new File("/path/to/image.jpg");
      FileDataSource imageSource = new FileDataSource(imageFile);
      imagePart.setDataHandler(new DataHandler(imageSource));
      imagePart.setHeader("Content-ID", "<image1>"); // 设置 Content-ID, 与 HTML 中的 cid 对应
      imagePart.setDisposition(MimeBodyPart.INLINE); // 设置为内嵌
      multipart.addBodyPart(imagePart);

      message.setContent(multipart);

      // … (发送邮件同上)

    • 3.8 常见 SMTP 服务器配置示例

      • Gmail (使用 App Password):
        • 在 Gmail 设置中启用 IMAP/POP (通常默认启用)。
        • 开启“两步验证”。
        • 生成一个“应用专用密码”。
        • 配置: props.put("mail.smtp.host", "smtp.gmail.com");
          props.put("mail.smtp.port", "587");
          props.put("mail.smtp.auth", "true");
          props.put("mail.smtp.starttls.enable", "true");
          // username 是你的完整 Gmail 地址
          // password 是你生成的应用专用密码 (不是你的 Gmail 密码)
      • QQ 邮箱:
        • 登录 QQ 邮箱网页版,在“设置”->“账户”中开启 POP3/SMTP 服务。可能需要短信验证。
        • 开启后会获得一个授权码 (不是邮箱密码)。
        • 配置: props.put("mail.smtp.host", "smtp.qq.com");
          props.put("mail.smtp.port", "465"); // 或 587 (SSL 或 STARTTLS)
          props.put("mail.smtp.auth", "true");
          props.put("mail.smtp.ssl.enable", "true"); // 对于端口 465
          // 或者
          // props.put("mail.smtp.port", "587");
          // props.put("mail.smtp.starttls.enable", "true"); // 对于端口 587
          // username 是你的完整 QQ 邮箱地址
          // password 是你获得的授权码
      • 163/126 邮箱: 类似 QQ 邮箱,开启 SMTP 服务,使用授权码。 props.put("mail.smtp.host", "smtp.163.com"); // 或 smtp.126.com
        props.put("mail.smtp.port", "465");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.ssl.enable", "true");
        // username 是你的完整邮箱地址
        // password 是你设置的授权码
      • 企业邮箱: 咨询你的企业邮箱管理员获取 SMTP 服务器地址、端口、加密方式、用户名 (可能是完整邮箱或用户名) 和密码 (可能是邮箱密码或特定密码)。

    4. 接收邮件 (POP3 / IMAP)

    • 4.1 设置 Session 属性 (接收侧) 创建 Properties 对象。

      Properties props = new Properties();

      • 4.1.1 POP3: props.put("mail.pop3.host", "pop.example.com"); // POP3服务器地址
        props.put("mail.pop3.port", "995"); // POP3端口 (110非加密, 995 SSL)
        props.put("mail.pop3.ssl.enable", "true"); // 启用SSL (端口995)
        // 或者使用 STARTTLS (较少见)
        // props.put("mail.pop3.port", "110");
        // props.put("mail.pop3.starttls.enable", "true");
      • 4.1.2 IMAP: props.put("mail.imap.host", "imap.example.com"); // IMAP服务器地址
        props.put("mail.imap.port", "993"); // IMAP端口 (143非加密, 993 SSL)
        props.put("mail.imap.ssl.enable", "true"); // 启用SSL (端口993)
        // 或者使用 STARTTLS
        // props.put("mail.imap.port", "143");
        // props.put("mail.imap.starttls.enable", "true");
        props.put("mail.imap.partialfetch", "false"); // 可选,控制是否只获取邮件部分内容

      创建带认证的 Session (与发送类似):

      Session session = Session.getInstance(props, new Authenticator() {
      @Override
      protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication("your_username@example.com", "your_password");
      }
      });

    • 4.2 连接到邮件存储 (Store)

      try (Store store = session.getStore("pop3")) { // 或 "imap"
      store.connect(username, password);
      System.out.println("成功连接到邮件存储");
      // … 后续操作文件夹
      } catch (MessagingException e) {
      e.printStackTrace();
      }

    • 4.3 打开邮箱文件夹 (Folder)

      try (Folder folder = store.getFolder("INBOX")) { // 指定文件夹名
      // 打开文件夹 (只读模式)
      folder.open(Folder.READ_ONLY); // 或 Folder.READ_WRITE 允许修改/删除邮件
      System.out.println("邮件总数: " + folder.getMessageCount());
      System.out.println("新邮件数: " + folder.getNewMessageCount()); // 仅 IMAP 支持新邮件计数
      // … 后续获取邮件
      } // try-with-resources 会自动关闭 Folder

      • 4.3.1 常见文件夹: "INBOX" (收件箱), "Sent" (已发送), "Drafts" (草稿), "Trash" (垃圾箱/已删除), "Spam" (垃圾邮件)。名称可能因服务器和语言设置而异。IMAP 支持列出所有文件夹 (store.getDefaultFolder().list())。
      • 4.3.2 文件夹访问模式:
        • Folder.READ_ONLY: 只读,不能修改邮件。
        • Folder.READ_WRITE: 可读写,可以修改邮件状态、删除邮件等。
      • 4.3.3 IMAP支持文件夹管理。原理:使用Folder类创建、重命名或移动文件夹。代码示例: Store store = session.getStore();
        store.connect("your-email@example.com", "your-password");

        // 获取根文件夹
        Folder root = store.getDefaultFolder();
        Folder[] folders = root.list(); // 列出所有文件夹

        // 创建新文件夹
        Folder newFolder = root.getFolder("Work");
        if (!newFolder.exists()) {
        newFolder.create(Folder.HOLDS_MESSAGES); // 创建文件夹
        }

        // 重命名文件夹
        Folder oldFolder = root.getFolder("Old");
        oldFolder.renameTo(newFolder);

        // 移动邮件到文件夹
        Message message = folder.getMessage(1); // 获取邮件
        folder.copyMessages(new Message[]{message}, newFolder); // 复制到新文件夹

        store.close();

    • 4.4 获取邮件列表

      // 获取所有邮件 (谨慎使用,邮件多时可能慢)
      Message[] messages = folder.getMessages();
      // 获取指定范围的邮件 (例如最新10封)
      int start = Math.max(1, folder.getMessageCount() – 9); // 邮件编号从1开始
      int end = folder.getMessageCount();
      Message[] recentMessages = folder.getMessages(start, end);

    • 4.5 解析邮件内容 (Message)

      for (Message message : messages) {
      // 4.5.1 获取基本信息
      Address[] fromAddresses = message.getFrom();
      String from = (fromAddresses != null && fromAddresses.length > 0) ? fromAddresses[0].toString() : "未知";
      Address[] toAddresses = message.getRecipients(Message.RecipientType.TO);
      String toList = (toAddresses != null) ? InternetAddress.toString(toAddresses) : "";
      String subject = message.getSubject();
      Date sentDate = message.getSentDate();

      System.out.println("邮件 #" + message.getMessageNumber());
      System.out.println("发件人: " + from);
      System.out.println("收件人: " + toList);
      System.out.println("主题: " + subject);
      System.out.println("发送日期: " + sentDate);

      // 4.5.2 获取纯文本正文
      Object content = message.getContent();
      if (content instanceof String) {
      // 简单文本邮件
      String textContent = (String) content;
      System.out.println("正文:\\n" + textContent);
      } else if (content instanceof MimeMultipart) {
      // 复杂邮件 (多部分)
      MimeMultipart multipart = (MimeMultipart) content;
      handleMultipart(multipart); // 调用处理多部分的方法 (见下文)
      }

      System.out.println("————————————");
      }

      处理多部分邮件的方法 (handleMultipart):

      private static void handleMultipart(MimeMultipart multipart) throws Exception {
      int partCount = multipart.getCount();
      for (int i = 0; i < partCount; i++) {
      BodyPart bodyPart = multipart.getBodyPart(i);
      String disposition = bodyPart.getDisposition();
      String contentType = bodyPart.getContentType();

      // 4.5.3 获取 HTML 正文 / 4.5.4 获取附件
      if (disposition != null && (disposition.equalsIgnoreCase(Part.ATTACHMENT) || disposition.equalsIgnoreCase(Part.INLINE))) {
      // 这是一个附件或内嵌资源
      String fileName = bodyPart.getFileName();
      if (fileName != null) {
      fileName = MimeUtility.decodeText(fileName); // 解码可能的编码文件名
      }
      System.out.println("附件/资源: " + fileName + " (类型: " + contentType + ")");

      // 保存附件到文件
      saveAttachment(bodyPart, "/path/to/save/dir", fileName);
      } else {
      // 可能是文本正文 (纯文本或 HTML)
      Object partContent = bodyPart.getContent();
      if (partContent instanceof String) {
      if (contentType.toLowerCase().contains("text/html")) {
      System.out.println("HTML 正文:\\n" + partContent);
      } else if (contentType.toLowerCase().contains("text/plain")) {
      System.out.println("纯文本正文:\\n" + partContent);
      } else {
      System.out.println("其他文本内容 (" + contentType + "):\\n" + partContent);
      }
      } else if (partContent instanceof MimeMultipart) {
      // 嵌套的多部分 (递归处理)
      handleMultipart((MimeMultipart) partContent);
      }
      }
      }
      }

      保存附件的方法 (saveAttachment):

      private static void saveAttachment(BodyPart bodyPart, String saveDir, String fileName) throws Exception {
      if (fileName == null || fileName.isEmpty()) {
      fileName = "attachment_" + System.currentTimeMillis() + ".dat";
      }
      File saveFile = new File(saveDir, fileName);
      try (InputStream is = bodyPart.getInputStream();
      FileOutputStream fos = new FileOutputStream(saveFile)) {
      byte[] buffer = new byte[4096];
      int bytesRead;
      while ((bytesRead = is.read(buffer)) != -1) {
      fos.write(buffer, 0, bytesRead);
      }
      System.out.println("附件已保存到: " + saveFile.getAbsolutePath());
      }
      }

    • 4.6 使用 POP3 接收邮件 (完整示例代码)

      import java.util.Properties;
      import javax.mail.*;
      import javax.mail.internet.InternetAddress;
      import javax.mail.internet.MimeUtility;

      public class POP3MailReceiver {

      public static void main(String[] args) {
      // 邮件服务器配置
      String host = "pop.example.com"; // POP3服务器
      int port = 995; // POP3 SSL端口
      String username = "your_username@example.com";
      String password = "your_password";
      String saveDir = "/path/to/save/attachments";

      Properties props = new Properties();
      props.put("mail.pop3.host", host);
      props.put("mail.pop3.port", port);
      props.put("mail.pop3.ssl.enable", "true");

      Session session = Session.getInstance(props, new Authenticator() {
      @Override
      protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(username, password);
      }
      });

      try (Store store = session.getStore("pop3")) {
      store.connect();

      try (Folder folder = store.getFolder("INBOX")) {
      folder.open(Folder.READ_ONLY);

      Message[] messages = folder.getMessages();
      System.out.println("收件箱中共有 " + messages.length + " 封邮件");

      for (Message message : messages) {
      // 解析并打印邮件基本信息 (如前面4.5.1部分)
      // …
      // 处理邮件内容 (调用 handleMultipart 或直接处理)
      Object content = message.getContent();
      if (content instanceof String) {
      System.out.println("正文:\\n" + content);
      } else if (content instanceof MimeMultipart) {
      handleMultipart((MimeMultipart) content, saveDir);
      }
      }
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }

      private static void handleMultipart(MimeMultipart multipart, String saveDir) throws Exception {
      // … (实现如前面 handleMultipart 方法)
      }

      private static void saveAttachment(BodyPart bodyPart, String saveDir, String fileName) throws Exception {
      // … (实现如前面 saveAttachment 方法)
      }
      }

    • 4.7 使用 IMAP 接收邮件 (完整示例代码) 结构与 POP3 示例非常相似,主要区别在于 Store 协议和 Folder 操作 (IMAP 支持更多功能如搜索、标记)。

      import java.util.Properties;
      import javax.mail.*;

      public class IMAPMailReceiver {

      public static void main(String[] args) {
      // 邮件服务器配置
      String host = "imap.example.com"; // IMAP服务器
      int port = 993; // IMAP SSL端口
      String username = "your_username@example.com";
      String password = "your_password";
      String saveDir = "/path/to/save/attachments";

      Properties props = new Properties();
      props.put("mail.imap.host", host);
      props.put("mail.imap.port", port);
      props.put("mail.imap.ssl.enable", "true");
      props.put("mail.imap.partialfetch", "false"); // 获取完整邮件

      Session session = Session.getInstance(props, new Authenticator() {
      @Override
      protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(username, password);
      }
      });

      try (Store store = session.getStore("imap")) {
      store.connect();

      try (Folder folder = store.getFolder("INBOX")) {
      folder.open(Folder.READ_ONLY);

      // 获取所有邮件
      Message[] messages = folder.getMessages();
      // 或者使用搜索
      // SearchTerm term = new SubjectTerm("重要");
      // Message[] messages = folder.search(term);

      for (Message message : messages) {
      // 解析邮件 (同 POP3 示例)
      // …
      }
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      // … handleMultipart 和 saveAttachment 方法同上
      }

    • 4.8 搜索邮件 (IMAP 优势) IMAP 支持强大的搜索功能。使用 SearchTerm 及其子类构建搜索条件。

      // 搜索主题包含 "报告" 的邮件
      SearchTerm subjectTerm = new SubjectTerm("报告");
      // 搜索来自特定发件人的邮件
      SearchTerm fromTerm = new FromTerm(new InternetAddress("boss@company.com"));
      // 搜索未读邮件
      SearchTerm unseenTerm = new FlagTerm(new Flags(Flags.Flag.SEEN), false);
      // 组合搜索条件 (AND)
      SearchTerm andTerm = new AndTerm(subjectTerm, fromTerm);
      // 组合搜索条件 (OR)
      SearchTerm orTerm = new OrTerm(subjectTerm, fromTerm);

      Message[] foundMessages = folder.search(searchTerm);

    • 4.9 删除邮件 (需要 READ_WRITE 模式)

      folder.open(Folder.READ_WRITE); // 必须用读写模式打开才能删除
      message.setFlag(Flags.Flag.DELETED, true); // 将邮件标记为删除
      // 立即删除标记为删除的邮件 (谨慎使用)
      folder.expunge();
      // 或者关闭文件夹时删除
      folder.close(true); // true 表示删除标记为 DELETED 的邮件

    • 4.10 邮件回复

           回复邮件时引用原内容。原理:创建新邮件,设置In-Reply-To头。

    Message originalMessage = folder.getMessage(1); // 获取原邮件
    Message reply = new MimeMessage(session);
    reply.setFrom(new InternetAddress("your-email@example.com"));
    reply.setRecipients(Message.RecipientType.TO, originalMessage.getFrom());
    reply.setSubject("Re: " + originalMessage.getSubject());
    // 引用原内容
    String content = "回复内容\\n\\n原邮件:\\n" + originalMessage.getContent();
    reply.setText(content);
    // 设置回复头
    reply.setHeader("In-Reply-To", originalMessage.getMessageID());
    Transport.send(reply);

    • 4.10 邮件抄送与密送

           抄送(CC)和密送(BCC)在发送时设置收件人类型。

    Message message = new MimeMessage(session);
    message.setRecipients(Message.RecipientType.TO, InternetAddress.parse("to@example.com"));
    message.setRecipients(Message.RecipientType.CC, InternetAddress.parse("cc@example.com")); // 抄送
    message.setRecipients(Message.RecipientType.BCC, InternetAddress.parse("bcc@example.com")); // 密送
    // 其余发送代码同第三章

    • 4.12 邮件转发

           转发邮件将原邮件作为附件发送。原理:创建新邮件,原邮件作为附件。

    Message originalMessage = folder.getMessage(1);
    Message forward = new MimeMessage(session);
    forward.setFrom(new InternetAddress("your-email@example.com"));
    forward.setRecipients(Message.RecipientType.TO, InternetAddress.parse("forward@example.com"));
    forward.setSubject("转发: " + originalMessage.getSubject());

    // 原邮件作为附件
    MimeBodyPart attachmentPart = new MimeBodyPart();
    attachmentPart.setContent(originalMessage, "message/rfc822"); // 设置邮件类型
    attachmentPart.setFileName("original.eml");

    Multipart multipart = new MimeMultipart();
    multipart.addBodyPart(attachmentPart);
    forward.setContent(multipart);
    Transport.send(forward);

    • 4.13 邮件附件下载

           接收邮件时下载附件。原理:检查邮件内容类型,如果为Multipart,遍历BodyPart获取附件。

    import javax.mail.*;
    import javax.mail.internet.*;
    import java.io.*;

    public class AttachmentDownloader {
    public static void main(String[] args) {
    // 使用IMAP接收邮件代码获取Message对象
    try {
    if (message.getContent() instanceof Multipart) {
    Multipart multipart = (Multipart) message.getContent();
    for (int i = 0; i < multipart.getCount(); i++) {
    BodyPart bodyPart = multipart.getBodyPart(i);
    if (Part.ATTACHMENT.equalsIgnoreCase(bodyPart.getDisposition())) {
    String fileName = bodyPart.getFileName();
    InputStream is = bodyPart.getInputStream();
    // 保存附件到本地
    FileOutputStream fos = new FileOutputStream("downloads/" + fileName);
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = is.read(buffer)) != -1) {
    fos.write(buffer, 0, bytesRead);
    }
    fos.close();
    is.close();
    System.out.println("附件下载: " + fileName);
    }
    }
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    }

    • 4.14 邮件标记已读与未读

           使用IMAP标记邮件状态。原理:设置邮件的SEEN标志。

    // 在接收邮件代码中,使用IMAP
    Message message = folder.getMessage(i); // 获取邮件
    if (!message.isSet(Flags.Flag.SEEN)) {
    message.setFlag(Flags.Flag.SEEN, true); // 标记为已读
    }

    • 4.15 邮件回执

           邮件回执(Delivery Status Notification)通知发件人邮件是否送达。原理:发送邮件时设置请求回执头,服务器返回状态报告。

    // 在发送邮件代码中,添加回执请求
    Message message = new MimeMessage(session);
    message.setHeader("Disposition-Notification-To", "your-email@example.com"); // 设置回执接收邮箱
    // 其余发送代码同第三章

    5. 处理复杂邮件结构与 MIME

    • 5.1 Multipart 深入理解

      • Multipart 是 Part (如 MimeMessage 或 MimeBodyPart) 内容的一种类型,表示该部分包含多个子部分 (BodyPart)。
      • 常见的 Multipart 子类型:
        • multipart/mixed: 包含独立的子部分。例如,一封包含正文文本和几个附件的邮件。
        • multipart/alternative: 包含表示同一信息的不同版本。例如,一封同时包含纯文本版本和 HTML 版本的邮件。邮件客户端会选择最适合显示给用户的版本。
        • multipart/related: 包含一个根部分 (通常是 HTML) 和与之相关的内嵌资源 (如图片)。根部分使用 cid 引用内嵌资源。
        • multipart/signed: 用于数字签名。
        • multipart/encrypted: 用于加密邮件。
      • 这些子类型可以嵌套。例如,一个 multipart/mixed 可能包含一个 multipart/alternative (用于正文) 和一个 attachment (用于附件)。
    • 5.2 MimeBodyPart 详解

      • 代表 Multipart 中的一个子部分。
      • 包含自己的头信息 (Content-Type, Content-Transfer-Encoding, Content-Disposition, Content-ID 等) 和内容。
      • 内容可以是:
        • 简单文本 (String)
        • 二进制数据 (InputStream / DataSource)
        • 另一个 Multipart (嵌套结构)
        • 一个 Message (表示作为附件包含的完整邮件 – message/rfc822)
    • 5.3 内容类型 (Content-Type) 详解

      • 格式:type/subtype; parameters (如 text/html; charset=UTF-8)。
      • 常见类型:
        • text/plain: 纯文本。
        • text/html: HTML 文本。
        • image/jpeg, image/png, image/gif: 图片。
        • application/pdf, application/msword, application/vnd.ms-excel: 应用程序文件。
        • application/octet-stream: 通用二进制数据。
        • multipart/*: 多部分类型 (如上所述)。
        • message/rfc822: 嵌入的完整邮件。
      • charset 参数对于文本类型非常重要 (如 charset=UTF-8, charset=GBK),指定字符编码,解决乱码问题。
    • 5.4 内容传输编码 (Content-Transfer-Encoding)

      • 用于将二进制或非 ASCII 邮件内容编码为适合在 7-bit SMTP 通道中传输的格式。
      • 常见编码:
        • 7bit: 内容已经是 7-bit ASCII,短行 (<1000字符)。
        • 8bit: 内容包含 8-bit 字符,但行不太长。
        • base64: 将二进制数据编码为 ASCII 字符 (字母、数字、+、/)。效率约为原始数据的 133%。适用于图像、可执行文件等。
        • quoted-printable: 将非 ASCII 字符和特殊字符编码为 = 后跟两个十六进制数字。适用于主要包含 ASCII 字符但包含少量非 ASCII 字符的文本 (如带有重音符号的欧洲语言)。行长度有限制。
    • 5.5 内容处置 (Content-Disposition)

      • 指示邮件客户端如何处理该部分。
      • 常见值:
        • inline: 该部分应在邮件正文中内嵌显示 (如 HTML 中的图片)。
        • attachment: 该部分应作为附件下载,不直接显示在邮件正文中。
      • 通常包含 filename 参数指定附件保存时的默认文件名 (如 Content-Disposition: attachment; filename="report.pdf")。文件名可能需要使用 MimeUtility.decodeText 解码。
    • 5.6 处理嵌套的 Multipart 结构 如前面 handleMultipart 方法所示,需要使用递归来遍历可能的多层嵌套结构。检查每个 BodyPart 的 Content-Type 是否是 multipart/*,如果是,则递归调用处理多部分的方法。

    6. 安全性与最佳实践

    • 6.1 SSL/TLS 加密的重要性

      • 始终使用 SSL/TLS (mail.smtp.ssl.enable / mail.smtp.starttls.enable / mail.pop3.ssl.enable / mail.imap.ssl.enable) 来加密邮件传输通道,防止用户名、密码和邮件内容被窃听。
      • 优先使用端口 465 (SMTP SSL), 993 (IMAP SSL), 995 (POP3 SSL) 或启用 STARTTLS 的端口 587 (SMTP), 143 (IMAP)。
    • 6.2 安全地存储密码

      • 切勿将密码硬编码在源代码中。
      • 使用安全的方式存储密码:
        • 环境变量。
        • 配置文件 (确保文件权限安全)。
        • 密钥管理系统 (如 HashiCorp Vault, AWS Secrets Manager)。
        • 在运行时提示用户输入 (对于命令行工具)。
      • 对于 Gmail 等,使用“应用专用密码”而非主密码。
    • 6.3 处理敏感信息

      • 避免在日志中记录完整的邮件内容、附件或敏感邮件头。
      • 对包含敏感数据的邮件正文和附件进行适当处理 (如脱敏、加密存储)。
    • 6.4 防范邮件注入攻击

      • 当使用用户输入构建邮件 (如发件人地址、主题、正文) 时,务必进行严格的输入验证和清理。
      • 避免直接将未经验证的用户输入拼接到 SMTP 命令或邮件头中。恶意输入可能注入额外的 SMTP 命令或修改邮件结构。
      • 使用 JavaMail API 的方法 (如 setFrom, setRecipients, setSubject, setText) 来设置字段,它们内部会进行一定的处理。但仍需对输入进行验证。
    • 6.5 连接池与资源管理

      • Transport 和 Store 对象代表网络连接,是有限资源。
      • 总是在使用完毕后关闭它们 (close() 方法)。
      • 使用 try-with-resources 语句 (Java 7+) 确保资源自动关闭,即使在发生异常时。
      • 如果需要频繁发送邮件,考虑使用连接池库 (如 Apache Commons Pool) 来管理 Transport 对象,而不是为每封邮件创建新连接。但需注意服务器可能限制并发连接数。
    • 6.6 错误处理与重试机制

      • 使用 try-catch 捕获 MessagingException 和其他可能的异常 (IOException, RuntimeException 等)。
      • 根据异常类型 (AuthenticationFailedException, SendFailedException, ConnectException 等) 进行不同的处理 (记录错误、通知用户、重试)。
      • 对于暂时性错误 (如网络波动、服务器繁忙),实现重试逻辑 (带退避策略,如指数退避)。
    • 6.7 日志记录

      • 使用日志框架 (如 SLF4J + Logback) 记录关键操作 (连接成功/失败、发送成功/失败、接收邮件数量、错误信息)。
      • 合理设置日志级别 (DEBUG 用于开发调试,INFO 用于常规操作,WARN/ERROR 用于问题和异常)。
      • 开启 mail.debug 属性时,JavaMail 会将详细的协议交互输出到控制台,对调试非常有帮助,但在生产环境中应关闭。

    7. 常见问题与故障排除

    • 7.1 认证失败 (AuthenticationFailedException)

      • 原因: 用户名或密码错误;服务器未启用 SMTP/POP3/IMAP 服务;需要应用专用密码 (如 Gmail) 或授权码 (如 QQ/163) 而使用了邮箱密码;账户被锁定。
      • 解决: 仔细检查用户名密码;确认邮箱设置中已开启相应服务;对于 Gmail/QQ/163 等,使用正确的授权方式;尝试在网页邮箱登录验证账户状态。
    • 7.2 连接超时 (ConnectException)

      • 原因: 服务器地址错误;端口错误;服务器宕机;网络不通;防火墙阻止了连接。
      • 解决: 验证服务器地址和端口;使用 telnet 或 nc 命令测试是否能连接到服务器的端口;检查网络连接;检查防火墙设置。
    • 7.3 SSL/TLS 证书问题 (SSLHandshakeException)

      • 原因: 服务器证书不受信任 (自签名证书、证书过期、域名不匹配);Java 运行环境缺少根证书。
      • 解决 (开发/测试): 对于自签名证书,可以临时信任所有证书 (不推荐生产环境): props.put("mail.smtp.ssl.trust", "*"); // 信任所有主机
        // 或者更精确地信任特定主机
        // props.put("mail.smtp.ssl.trust", "smtp.example.com");
      • 解决 (生产): 将服务器的有效证书导入 Java 的信任库 (cacerts) 或配置应用使用包含该证书的信任库。
    • 7.4 端口被阻止 (防火墙)

      • 原因: 本地防火墙或公司网络防火墙阻止了到邮件服务器端口的出站连接。
      • 解决: 联系网络管理员,请求开放相应的 SMTP (25, 465, 587) / POP3 (110, 995) / IMAP (143, 993) 端口。
    • 7.5 附件无法下载或显示

      • 原因: Content-Disposition 设置不正确;Content-Type 识别错误;文件名编码问题;保存路径权限问题;邮件本身损坏。
      • 解决: 检查代码中附件部分的处理和保存逻辑;使用 MimeUtility.decodeText 解码文件名;检查保存目录是否存在且有写入权限;尝试用其他邮件客户端下载同一附件看是否正常。
    • 7.6 HTML 邮件样式丢失

      • 原因: 邮件客户端对 HTML/CSS 的支持有限 (特别是移动客户端);样式使用了外部 CSS (通常被阻止);样式使用了客户端不支持的 CSS 属性;内嵌图片未正确加载 (cid 引用错误)。
      • 解决: 使用内联 CSS (style="…" 属性);避免使用复杂的 CSS;提供纯文本备选版本 (multipart/alternative);确保内嵌图片的 Content-ID 设置正确且在 HTML 中引用正确。
    • 7.7 中文乱码问题

      • 原因: 邮件头 (主题、发件人/收件人名称) 或正文未正确指定字符集;使用的字符集与邮件客户端解码字符集不一致;附件文件名编码问题。
      • 解决: 在设置主题、正文、附件文件名时,显式指定正确的字符集 (通常是 UTF-8): message.setSubject("中文主题", "UTF-8"); // 设置主题和字符集
        message.setText("中文正文", "UTF-8"); // 纯文本
        // HTML
        message.setContent("<p>中文</p>", "text/html; charset=UTF-8");
        // 附件文件名
        attachmentPart.setFileName(MimeUtility.encodeText("中文文件名.pdf", "UTF-8", "B")); // 使用 Base64 或 Q 编码
        接收时,使用 MimeUtility.decodeText 解码可能被编码的字符串。

    8. 完整系统案例

    • 案例一:邮件发送工具类 (支持文本、HTML、附件、图片)

      import javax.mail.*;
      import javax.mail.internet.*;
      import javax.activation.*;
      import java.util.*;

      public class MailSender {

      private Session session;

      public MailSender(String host, int port, final String username, final String password, boolean useSSL, boolean useStartTLS) {
      Properties props = new Properties();
      props.put("mail.smtp.host", host);
      props.put("mail.smtp.port", port);
      props.put("mail.smtp.auth", "true");
      if (useSSL) {
      props.put("mail.smtp.ssl.enable", "true");
      } else if (useStartTLS) {
      props.put("mail.smtp.starttls.enable", "true");
      }

      this.session = Session.getInstance(props, new Authenticator() {
      @Override
      protected PasswordAuthentication getPasswordAuthentication() {
      return new PasswordAuthentication(username, password);
      }
      });
      }

      public void sendTextMail(String from, String to, String subject, String text) throws MessagingException {
      MimeMessage message = new MimeMessage(session);
      message.setFrom(new InternetAddress(from));
      message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
      message.setSubject(subject, "UTF-8");
      message.setText(text, "UTF-8");
      Transport.send(message);
      }

      public void sendHtmlMail(String from, String to, String subject, String htmlContent) throws MessagingException {
      MimeMessage message = new MimeMessage(session);
      message.setFrom(new InternetAddress(from));
      message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
      message.setSubject(subject, "UTF-8");
      message.setContent(htmlContent, "text/html; charset=UTF-8");
      Transport.send(message);
      }

      public void sendMailWithAttachment(String from, String to, String subject, String text, List<Attachment> attachments) throws MessagingException {
      MimeMessage message = new MimeMessage(session);
      message.setFrom(new InternetAddress(from));
      message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
      message.setSubject(subject, "UTF-8");

      MimeMultipart multipart = new MimeMultipart();

      // 文本部分
      MimeBodyPart textPart = new MimeBodyPart();
      textPart.setText(text, "UTF-8");
      multipart.addBodyPart(textPart);

      // 附件部分
      for (Attachment attachment : attachments) {
      MimeBodyPart attachmentPart = new MimeBodyPart();
      attachmentPart.setDataHandler(new DataHandler(new FileDataSource(attachment.getFilePath())));
      attachmentPart.setFileName(MimeUtility.encodeText(attachment.getFileName(), "UTF-8", "B"));
      multipart.addBodyPart(attachmentPart);
      }

      message.setContent(multipart);
      Transport.send(message);
      }

      public void sendMailWithInlineImages(String from, String to, String subject, String htmlContent, Map<String, String> cidImageMap) throws MessagingException {
      MimeMessage message = new MimeMessage(session);
      message.setFrom(new InternetAddress(from));
      message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to));
      message.setSubject(subject, "UTF-8");

      MimeMultipart multipart = new MimeMultipart("related");

      // HTML 部分
      MimeBodyPart htmlPart = new MimeBodyPart();
      htmlPart.setContent(htmlContent, "text/html; charset=UTF-8");
      multipart.addBodyPart(htmlPart);

      // 图片部分
      for (Map.Entry<String, String> entry : cidImageMap.entrySet()) {
      String cid = entry.getKey();
      String imagePath = entry.getValue();
      MimeBodyPart imagePart = new MimeBodyPart();
      imagePart.setDataHandler(new DataHandler(new FileDataSource(imagePath)));
      imagePart.setContentID("<" + cid + ">");
      imagePart.setDisposition(MimeBodyPart.INLINE);
      multipart.addBodyPart(imagePart);
      }

      message.setContent(multipart);
      Transport.send(message);
      }

      public static class Attachment {
      private String filePath;
      private String fileName;

      public Attachment(String filePath, String fileName) {
      this.filePath = filePath;
      this.fileName = fileName;
      }
      // Getters… (省略)
      }
      }

    • 案例二:邮件接收监控服务 (IMAP IDLE/Polling) 这是一个更复杂的案例,涉及持续监控邮箱 (使用 IMAP IDLE 指令或定时轮询)。

      import javax.mail.*;
      import javax.mail.event.MessageCountEvent;
      import javax.mail.event.MessageCountListener;
      import java.util.concurrent.Executors;
      import java.util.concurrent.ScheduledExecutorService;
      import java.util.concurrent.TimeUnit;

      public class MailMonitorService {

      private final String host;
      private final int port;
      private final String username;
      private final String password;
      private Store store;
      private Folder inbox;
      private ScheduledExecutorService scheduler;

      public MailMonitorService(String host, int port, String username, String password) {
      this.host = host;
      this.port = port;
      this.username = username;
      this.password = password;
      }

      public void start() throws MessagingException {
      Properties props = new Properties();
      props.put("mail.imap.host", host);
      props.put("mail.imap.port", port);
      props.put("mail.imap.ssl.enable", "true");

      Session session = Session.getInstance(props);
      store = session.getStore("imap");
      store.connect(username, password);

      inbox = store.getFolder("INBOX");
      inbox.open(Folder.READ_WRITE); // 需要读写权限来标记邮件

      // 方式一:使用 IMAP IDLE (服务器推送新邮件通知)
      try {
      if (inbox instanceof IMAPFolder) {
      IMAPFolder imapFolder = (IMAPFolder) inbox;
      imapFolder.addMessageCountListener(new MessageCountListener() {
      @Override
      public void messagesAdded(MessageCountEvent e) {
      Message[] newMessages = e.getMessages();
      processNewMessages(newMessages);
      }
      @Override
      public void messagesRemoved(MessageCountEvent e) {
      // 处理邮件删除事件
      }
      });
      // 进入 IDLE 模式
      imapFolder.idle();
      } else {
      throw new IllegalStateException("IDLE only supported with IMAPFolder");
      }
      } catch (Exception e) {
      // 如果 IDLE 失败或不可用,回退到轮询
      startPolling();
      }

      // 方式二:定时轮询 (如果 IDLE 不可用或失败)
      // startPolling();
      }

      private void startPolling() {
      scheduler = Executors.newSingleThreadScheduledExecutor();
      scheduler.scheduleAtFixedRate(() -> {
      try {
      // 检查是否有新邮件
      if (inbox.hasNewMessages()) { // 这个方法可能不可靠
      // 更可靠的方式:记录上次检查的UID或邮件数量
      int oldCount = …; // 保存上次的邮件数量或最高 UID
      Message[] newMessages = inbox.getMessages(); // 或者根据 UID 获取新邮件
      processNewMessages(newMessages);
      // 更新 oldCount 或 UID
      }
      } catch (MessagingException e) {
      e.printStackTrace();
      // 处理错误,可能需要重新连接
      }
      }, 0, 60, TimeUnit.SECONDS); // 每 60 秒检查一次
      }

      private void processNewMessages(Message[] messages) {
      for (Message message : messages) {
      try {
      // 解析邮件内容 (同前面接收邮件部分)
      // …
      System.out.println("收到新邮件: " + message.getSubject());
      // 处理邮件业务逻辑…
      // 标记为已读 (可选)
      message.setFlag(Flags.Flag.SEEN, true);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

      public void stop() {
      try {
      if (inbox != null && inbox.isOpen()) {
      inbox.close(false); // 不删除标记为 DELETED 的邮件
      }
      if (store != null && store.isConnected()) {
      store.close();
      }
      if (scheduler != null) {
      scheduler.shutdown();
      }
      } catch (MessagingException e) {
      e.printStackTrace();
      }
      }
      }

    • 案例三:简单的邮件客户端 (命令行或 Swing) 这是一个较大的项目框架,涉及用户界面 (命令行菜单或 Swing GUI)、邮件列表展示、邮件查看、发送邮件等功能。核心逻辑复用前面发送和接收邮件的代码块。这里提供一个概念设计:

      • 功能模块:
        • 配置管理 (保存/加载多个邮箱账号配置)。
        • 发送邮件界面 (输入收件人、主题、正文、添加附件)。
        • 接收邮件列表 (展示收件箱邮件主题、发件人、日期)。
        • 邮件查看器 (显示邮件正文、附件列表、下载附件)。
        • 连接管理 (连接/断开 IMAP/POP3/SMTP)。
      • 技术选型:
        • 命令行: Java 控制台输入输出。
        • GUI: Java Swing 或 JavaFX。
      • 核心挑战: 邮件列表的实时刷新 (如使用 MailMonitorService)、富文本正文的渲染、附件的预览等。
    • 案例四:Spring Boot 集成邮件发送 Spring Boot 提供了强大的邮件发送支持 (spring-boot-starter-mail)。 依赖 (Maven):

      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-mail</artifactId>
      </dependency>

      配置文件 (application.properties):

      spring.mail.host=smtp.example.com
      spring.mail.port=587
      spring.mail.username=your_username@example.com
      spring.mail.password=your_password
      spring.mail.properties.mail.smtp.auth=true
      spring.mail.properties.mail.smtp.starttls.enable=true
      # 可选调试
      spring.mail.properties.mail.debug=true

      使用 JavaMailSender:

      import org.springframework.mail.SimpleMailMessage;
      import org.springframework.mail.javamail.JavaMailSender;
      import org.springframework.mail.javamail.MimeMessageHelper;
      import org.springframework.stereotype.Service;
      import javax.mail.MessagingException;
      import javax.mail.internet.MimeMessage;
      import java.io.File;

      @Service
      public class EmailService {

      private final JavaMailSender mailSender;

      public EmailService(JavaMailSender mailSender) {
      this.mailSender = mailSender;
      }

      public void sendSimpleMail(String to, String subject, String text) {
      SimpleMailMessage message = new SimpleMailMessage();
      message.setTo(to);
      message.setSubject(subject);
      message.setText(text);
      mailSender.send(message);
      }

      public void sendHtmlMail(String to, String subject, String htmlContent) throws MessagingException {
      MimeMessage mimeMessage = mailSender.createMimeMessage();
      MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); // true for multipart
      helper.setTo(to);
      helper.setSubject(subject);
      helper.setText(htmlContent, true); // true indicates HTML
      mailSender.send(mimeMessage);
      }

      public void sendMailWithAttachment(String to, String subject, String text, File attachment) throws MessagingException {
      MimeMessage mimeMessage = mailSender.createMimeMessage();
      MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
      helper.setTo(to);
      helper.setSubject(subject);
      helper.setText(text);
      helper.addAttachment(attachment.getName(), attachment);
      mailSender.send(mimeMessage);
      }

      public void sendMailWithInlineImage(String to, String subject, String htmlContent, File image, String cid) throws MessagingException {
      MimeMessage mimeMessage = mailSender.createMimeMessage();
      MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, "UTF-8");
      helper.setTo(to);
      helper.setSubject(subject);
      helper.setText(htmlContent, true);
      helper.addInline(cid, image); // cid 是 HTML 中引用的 Content-ID (不带 < >)
      mailSender.send(mimeMessage);
      }
      }

      调用示例 (在 Controller 或 Service 中):

      @Autowired
      private EmailService emailService;

      public void sendWelcomeEmail(String userEmail) {
      try {
      emailService.sendHtmlMail(userEmail, "欢迎加入", "<h1>欢迎!</h1><p>感谢注册…</p>");
      } catch (MessagingException e) {
      // 处理异常
      }
      }


    这份指南涵盖了 Java 邮件编程的绝大部分核心内容,从基础协议、API 使用到高级技巧、安全实践和完整案例。希望它能成为你开发邮件相关应用的权威参考手册。请根据实际需求选择适用的部分进行实践。

    赞(0)
    未经允许不得转载:171主机测评 » Java邮件编程全指南:从入门到精通
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址