一、引言

在数字化时代,邮件作为跨场景、高可靠性的信息传递载体,早已深度融入企业业务与个人生活 —— 从用户注册时的身份验证邮件、电商平台的订单通知,到企业内部的流程审批提醒,背后都离不开简单邮件传输协议(SMTP)的支撑。作为后端开发领域的主流语言,Java 凭借成熟的生态体系与稳定的性能,成为实现 SMTP 邮件发送功能的优选技术栈,其提供的 Jakarta Mail(原 JavaMail)工具包更是将复杂的协议交互封装为简洁的 API,让开发者无需深入理解 SMTP 底层细节,即可快速构建邮件发送能力。

然而,在实际开发中,开发者常面临环境配置繁琐、认证机制适配(如 SSL/TLS 加密)、邮件内容(文本 / 附件 / HTML)构建不规范、跨邮箱服务商(如 QQ 邮箱、企业微信邮箱)兼容性差等问题。本文将以 “实用落地” 为核心,从 SMTP 协议的核心工作原理切入,结合 Java 技术栈的实践特性,逐步拆解邮件发送的全流程:先讲解 Jakarta Mail 的环境搭建与依赖配置,再深入剖析邮件会话创建、发送者认证、邮件内容组装、发送逻辑实现等关键步骤,同时针对开发中常见的 “认证失败”“附件乱码”“发送超时” 等问题提供解决方案,帮助开发者高效掌握基于 Java 实现 SMTP 邮件发送的核心能力,为企业级应用的自动化邮件需求提供可落地的技术参考。

二、源码

1、maven

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lee</groupId>
    <artifactId>EmailSender</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
       <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.2</version>
        </dependency>
    </dependencies>
</project>

2、EmailSender.java

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import java.io.File;
import java.io.IOException;
import java.util.Properties;

/**
 * @author Lee
 * @date 2025-08-15
 */
public class EmailSender {

    /**
     * 发送带抄送、密送和附件的邮件
     *
     * @param host         SMTP服务器地址
     * @param port         SMTP服务器端口
     * @param username     发件人邮箱账号
     * @param password     发件人邮箱密码或授权码
     * @param toAddresses  收件人邮箱地址数组
     * @param ccAddresses  抄送人邮箱地址数组(可为null)
     * @param bccAddresses 密送人邮箱地址数组(可为null)
     * @param subject      邮件主题
     * @param content      邮件正文内容
     * @param filePaths    附件文件路径数组(可为null)
     * @throws MessagingException 邮件发送相关异常
     * @throws IOException        附件处理相关IO异常
     */
    public static void sendEmail(String host, String port, final String username, final String password,
                                 String[] toAddresses, String[] ccAddresses, String[] bccAddresses,
                                 String subject, String content, String[] filePaths)
    throws MessagingException, IOException {

        // 1. 设置邮件服务器属性
        Properties props = new Properties();
        props.put("mail.smtp.host", host);          // SMTP服务器地址
        props.put("mail.smtp.port", port);          // SMTP服务器端口
        props.put("mail.smtp.auth", "true");        // 需要身份验证
        props.setProperty("mail.smtp.ssl.enable", "true");   // 启用SSL

        // 2. 创建邮箱验证器,用于服务器身份验证
        Authenticator auth = new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(username, password);
            }
        };

        // 3. 获取邮件会话
        Session session = Session.getInstance(props, auth);
        session.setDebug(true); // 调试模式,true时会打印详细发送日志

        // 4. 创建邮件消息对象
        MimeMessage message = new MimeMessage(session);
        message.setFrom(new InternetAddress(username)); // 设置发件人

        // 5. 设置收件人
        if (toAddresses != null && toAddresses.length > 0) {
            InternetAddress[] to = new InternetAddress[toAddresses.length];
            for (int i = 0; i < toAddresses.length; i++) {
                to[i] = new InternetAddress(toAddresses[i]);
            }
            message.setRecipients(Message.RecipientType.TO, to); // TO:直接收件人
        }

        // 6. 设置抄送人
        if (ccAddresses != null && ccAddresses.length > 0) {
            InternetAddress[] cc = new InternetAddress[ccAddresses.length];
            for (int i = 0; i < ccAddresses.length; i++) {
                cc[i] = new InternetAddress(ccAddresses[i]);
            }
            message.setRecipients(Message.RecipientType.CC, cc); // CC:抄送
        }

            // 7. 设置密送人
            if (bccAddresses != null && bccAddresses.length > 0) {
            InternetAddress[] bcc = new InternetAddress[bccAddresses.length];
            for (int i = 0; i < bccAddresses.length; i++) {
                bcc[i] = new InternetAddress(bccAddresses[i]);
            }
            message.setRecipients(Message.RecipientType.BCC, bcc); // BCC:密送
        }

        // 8. 设置邮件主题
        message.setSubject(subject);

        // 9. 创建多部分内容(用于同时包含正文和附件)
        Multipart multipart = new MimeMultipart();

        // 10. 创建正文部分
        BodyPart messageBodyPart = new MimeBodyPart();
        messageBodyPart.setText(content); // 设置正文内容
        multipart.addBodyPart(messageBodyPart); // 将正文添加到多部分内容

        // 11. 添加附件(如果有)
        if (filePaths != null && filePaths.length > 0) {
            for (String filePath : filePaths) {
                // 创建附件部分
                BodyPart attachmentBodyPart = new MimeBodyPart();
                File file = new File(filePath);

                // 检查文件是否存在
                if (!file.exists() || !file.isFile()) {
                    throw new IOException("附件文件不存在或不是有效文件: " + filePath);
                }

                // 配置附件数据源
                DataSource source = new FileDataSource(file);
                attachmentBodyPart.setDataHandler(new DataHandler(source));
                // 设置附件文件名(使用MimeUtility.encodeWord处理中文文件名)
                attachmentBodyPart.setFileName(javax.mail.internet.MimeUtility.encodeWord(file.getName()));

                // 将附件添加到多部分内容
                multipart.addBodyPart(attachmentBodyPart);
            }
        }

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

        // 13. 发送邮件
        Transport.send(message);
        System.out.println("邮件发送成功!");
    }

    public static void main(String[] args) {
        // todo 邮件服务器配置(以163为例,其他邮箱请修改对应参数)
        String host = "smtp.163.com";
        String port = "465";
        String username = "159xxxx819@163.com"; // 发件人邮箱
        String password = "****************";    // SMTP授权码

        // 收件人信息
        String[] toAddresses = {"182xxxx137@163.com"};   // 主收件人
        String[] ccAddresses = {"252xxxx428@gmail.com"};   // 抄送人
        String[] bccAddresses = {"157xxxx373@qq.com"};  // 密送人
        String subject = "带抄送、密送和附件的测试邮件";
        String content = "这是一封使用Java发送的带抄送、密送和附件的测试邮件。";

        // 附件路径(可指定多个附件)
        String[] filePaths = {};

        try {
            // 发送邮件
            sendEmail(host, port, username, password,
                    toAddresses, ccAddresses, bccAddresses,
                    subject, content, filePaths);
        } catch (MessagingException e) {
            System.out.println("邮件发送失败:" + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            System.out.println("附件处理错误:" + e.getMessage());
            e.printStackTrace();
        }
    }
}
    

3、效果

4 总结解决步骤

  1. 确认 163 邮箱已开启 SMTP 服务并获取正确授权码
  2. 验证代码中host(smtp.163.com)、port(465 或 587)和加密配置是否匹配
  3. 检查网络连通性和端口是否被防火墙屏蔽
  4. 开启调试模式分析详细日志

三、拓展知识

1、用途

要理解 IMAP、SMTP、POP3 三个协议,核心需先明确其定位:SMTP 负责 “发送” 邮件,IMAP 和 POP3 负责 “接收” 邮件,三者协同实现完整的邮件收发流程

2、IMAP/SMTP/POP3 协议核心信息对比表

对比维度 SMTP (Simple Mail Transfer Protocol) 简单邮件传输协议 POP3 (Post Office Protocol 3) 邮局协议版本 3 IMAP (Internet Message Access Protocol) 互联网消息访问协议
核心定位 邮件「发送 / 传输」协议(仅负责 “投递”,不负责接收 / 存储) 邮件「接收」协议(本地下载型,服务器仅临时存储) 邮件「接收」协议(服务器同步型,本地与服务器实时联动)
核心用途 1. 从「发件人邮箱服务器」向「收件人邮箱服务器」传输邮件 2. 跨服务器接力投递(如 Gmail 向 QQ 邮箱发信) 3. 支持邮件客户端(如 Outlook、雷鸟)或网页端发送邮件 1. 将邮件从「收件人服务器」完整下载到「本地设备」(如电脑 / 手机) 2. 下载后可选择删除服务器副本,仅本地保留邮件 3. 适合单设备管理邮件的场景 1. 本地设备与服务器「同步邮件」(不强制完整下载,可只下载标题 / 摘要) 2. 本地操作(读 / 删 / 标星 / 建文件夹)实时同步到服务器 3. 支持多设备(电脑 + 手机 + 平板)协同访问邮件
工作原理要点 1. 依赖「邮件传输代理(MTA)」接力传递(如发件服务器→中转服务器→收件服务器) 2. 不处理邮件的 “读取” 或 “存储”,仅确保邮件到达目标服务器 3. 默认端口:25(非加密)、465(SSL 加密)、587(TLS 加密) 1. 设备连接服务器后,默认下载「全部未读邮件」到本地 2. 下载完成后,服务器可按设置保留 / 删除副本(默认多为 “保留”) 3. 默认端口:110(非加密)、995(SSL 加密) 4. 仅支持 “收件箱” 同步,不支持其他文件夹(如草稿箱、已发送) 1. 本地设备仅同步「邮件元数据」(标题、发件人),打开邮件时才下载正文 / 附件 2. 本地与服务器实时双向同步: - 手机删邮件→电脑端同步删除 - 电脑标 “已读”→手机端同步标记 3. 默认端口:143(非加密)、993(SSL 加密) 4. 支持所有文件夹(收件箱 / 草稿 / 已删)同步
典型应用场景 1. 所有邮箱服务的 “发送功能”(如 QQ 邮箱、Gmail 发邮件) 2. 邮件客户端(Outlook、Foxmail)配置发件功能 3. 企业邮件系统的跨域投递(如公司邮箱向客户邮箱发信) 1. 单设备使用(如仅用旧电脑查收邮件,无需多设备同步) 2. 流量 / 存储有限的场景(如旧手机,仅下载关键邮件) 3. 希望邮件仅本地保存,不占用服务器空间的需求 1. 多设备协同(如电脑办公 + 手机通勤查邮件,需同步状态) 2. 工作邮箱(需随时同步邮件进度,避免误删) 3. 对邮件安全性要求高(本地误删可从服务器恢复)
优点 1. 标准化程度极高,所有邮箱服务商均支持 2. 支持加密传输(SSL/TLS),防邮件被窃取 3. 可通过 “中继服务器” 解决发件 IP 黑名单问题 1. 本地访问速度快(无需依赖网络加载) 2. 节省服务器存储空间(下载后可删除服务器副本) 3. 对网络依赖低(断网可查看已下载邮件) 1. 多设备实时同步,操作一致性强 2. 邮件安全存储在服务器(本地设备损坏 / 丢失,邮件不丢失) 3. 支持选择性下载(仅下载标题,节省流量)
缺点 1. 仅负责 “传输”,不处理接收 / 存储(需配合 IMAP/POP3 用) 2. 易受垃圾邮件 / 钓鱼邮件滥用(需配合反垃圾机制) 1. 多设备无法同步(如手机删邮件,电脑仍显示) 2. 本地文件丢失 / 损坏则邮件无法恢复 3. 不支持文件夹同步(仅能同步收件箱) 1. 依赖网络(断网无法访问未同步的邮件内容) 2. 长期占用服务器存储空间(需定期清理) 3. 对服务器性能要求较高(需实时处理同步请求)

3、 三者如何协同工作?

  1. 发送邮件:你在 Outlook 写好邮件点击 “发送”→ 客户端通过 SMTP 协议将邮件发送到你的邮箱服务器(如 Gmail 服务器)→ 服务器再通过 SMTP 接力投递到收件人的邮箱服务器(如 QQ 邮箱服务器)。
  2. 接收邮件:收件人的邮箱服务器存储邮件后→ 收件人通过 Outlook 客户端,用 IMAP 或 POP3 协议从服务器获取邮件:
    • 选 IMAP:多设备同步查看,操作实时同步到服务器;
    • 选 POP3:仅下载到当前设备,多设备间无关联。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐