SMTP 之 Java 调用示例

本文介绍使用 Javamail 通过 SMTP 协议发信。

<dependency>
    <groupId>com.sun.mail</groupId>
    <artifactId>javax.mail</artifactId>
    <version>1.6.2</version>
</dependency>
<dependency>
    <groupId>javax.activation</groupId>
    <artifactId>activation</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>

示例代码:

package org.example;

import javax.mail.*;
import javax.mail.internet.*;
import java.io.UnsupportedEncodingException;
//import java.net.MalformedURLException;
import java.util.Date;
import java.util.Properties;
import java.util.UUID;
//import java.util.HashMap;
//import java.util.Base64;
//import java.net.URL;
//import java.net.URLEncoder;
//import javax.activation.DataHandler;
//import javax.activation.FileDataSource;
//import javax.activation.URLDataSource;

//import com.google.gson.GsonBuilder;

public class SampleMail {
    // 配置常量
    private static final String SMTP_HOST = "smtpdm.aliyun.com";
    private static final int SMTP_PORT = 80;
    private static final String USER_NAME = "发信地址";
    private static final String PASSWORD = "xxxxxxx";

    protected static String genMessageID(String mailFrom) {
        // 生成Message-ID:
        if (!mailFrom.contains("@")) {
            throw new IllegalArgumentException("Invalid email format: " + mailFrom);
        }
        String domain = mailFrom.split("@")[1];
        UUID uuid = UUID.randomUUID();
        return "<" + uuid.toString() + "@" + domain + ">";
    }

    private static void setRecipients(MimeMessage message, Message.RecipientType type, String[] recipients)
            throws MessagingException {
        // 设置收件人地址
        if (recipients == null || recipients.length == 0) {
            return; // 空列表不设置
        }
        InternetAddress[] addresses = new InternetAddress[recipients.length];
        for (int h = 0; h < recipients.length; h++) {
            addresses[h] = new InternetAddress(recipients[h]);
        }
        message.setRecipients(type, addresses);
    }

    public static void main(String[] args) throws MessagingException, UnsupportedEncodingException {
        // 配置发送邮件的环境属性
        final Properties props = new Properties();

        // 表示SMTP发送邮件,需要进行身份验证
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.host", SMTP_HOST);
        //设置端口:
        props.put("mail.smtp.port", SMTP_PORT);//或"25", 如果使用ssl,则去掉使用80或25端口的配置,进行如下配置:
        //加密方式:
        //props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
        //props.put("mail.smtp.socketFactory.fallback", "false");//禁止回退非加密
        //props.put("mail.smtp.socketFactory.port", "465");
        //props.put("mail.smtp.port", "465");

        props.put("mail.smtp.from", USER_NAME);    //mailfrom 参数
        props.put("mail.user", USER_NAME);// 发件人的账号(在控制台创建的发信地址)
        props.put("mail.password", PASSWORD);// 发信地址的smtp密码(在控制台选择发信地址进行设置)
        //props.put("mail.smtp.connectiontimeout", 1000);
        System.setProperty("mail.mime.splitlongparameters", "false");//用于解决附件名过长导致的显示异常
        //props.setProperty("mail.smtp.ssl.enable", "true");  //请配合465端口使用

        // 构建授权信息,用于进行SMTP进行身份验证
        Authenticator authenticator = new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(USER_NAME, PASSWORD);
            }
        };

        //使用环境属性和授权信息,创建邮件会话
        Session mailSession = Session.getInstance(props, authenticator);

        String messageIDValue = genMessageID(USER_NAME);
        MimeMessage message = new MimeMessage(mailSession) {
            @Override
            protected void updateMessageID() throws MessagingException {
                setHeader("Message-ID", messageIDValue);
            }
        };

        try {
            // 设置发件人邮件地址和名称。填写控制台配置的发信地址。和上面的mail.user保持一致。名称用户可以自定义填写。
            InternetAddress from = new InternetAddress(USER_NAME, "发件人昵称");//from 参数,可实现代发,注意:代发容易被收信方拒信或进入垃圾箱。
            message.setFrom(from);

            setRecipients(message, Message.RecipientType.TO, new String[]{"收信地址1", "收信地址2"});
            setRecipients(message, Message.RecipientType.CC, new String[]{"收信地址3", "收信地址4"});
            setRecipients(message, Message.RecipientType.BCC, new String[]{"收信地址5", "收信地址6"});

            InternetAddress replyToAddress = new InternetAddress("回信地址");
            message.setReplyTo(new Address[]{replyToAddress});//可选。设置回信地址
            message.setSentDate(new Date());
            message.setSubject("测试主题");
//            message.setContent("测试txt内容1", "text/text;charset=UTF-8");//纯文本内容,若使用MimeBodyPart这里会被覆盖
//            或
//            message.setContent("测试<br> html内容2", "text/html;charset=UTF-8");//HTML内容,若使用MimeBodyPart这里会被覆盖

//            //若需要开启邮件跟踪服务,请使用以下代码设置跟踪链接头。前置条件和约束见文档"如何开启数据跟踪功能?"
//            String tagName = "tagname4";
//            HashMap<String, String> trace = new HashMap<>();
//            //这里为字符串"1"
//            trace.put("OpenTrace", "1");      //打开邮件跟踪
//            trace.put("LinkTrace", "1");     //点击邮件里的URL跟踪
//            trace.put("TagName", tagName);   //控制台创建的标签tagname
//            String jsonTrace = new GsonBuilder().setPrettyPrinting().create().toJson(trace);
//            //System.out.println(jsonTrace);
//            String base64Trace = new String(Base64.getEncoder().encode(jsonTrace.getBytes()));
//            //设置跟踪链接头
//            message.addHeader("X-AliDM-Trace", base64Trace);
            //邮件eml原文中的示例值:X-AliDM-Trace: eyJUYWdOYW1lIjoiVGVzdCIsIk9wZW5UcmFjZSI6IjEiLCJMaW5rVHJhY2UiOiIxIn0=

            //发送附件和内容:
            // 创建多重消息
            Multipart multipart = new MimeMultipart();

//            // 创建一个BodyPart用于纯文本内容
//            BodyPart textPart = new MimeBodyPart();
//            textPart.setText("测试txt内容3");
//            multipart.addBodyPart(textPart);

            // 创建一个BodyPart用于HTML内容
            BodyPart htmlPart = new MimeBodyPart();
            htmlPart.setContent("测试<br> html内容4", "text/html;charset=UTF-8");//设置邮件的内容,会覆盖前面的message.setContent
            multipart.addBodyPart(htmlPart);

//            //附件部分
//            //发送附件,总的邮件大小不超过15M,创建消息部分。
//            // 发送本地附件
//            String[] fileList = {"C:\\Users\\Downloads\\test1.txt", "C:\\Users\\Downloads\\test2.txt"};
//            for (String filePath : fileList) {
//                MimeBodyPart mimeBodyPart = new MimeBodyPart();
//
//                FileDataSource fileDataSource = new FileDataSource(filePath);
//                mimeBodyPart.setDataHandler(new DataHandler(fileDataSource));
//                //处理附件名称中文(附带文件路径)乱码问题
//                mimeBodyPart.setFileName(MimeUtility.encodeWord(fileDataSource.getName()));
//                mimeBodyPart.addHeader("Content-Transfer-Encoding", "base64");
//                multipart.addBodyPart(mimeBodyPart);
//            }


//            // 发送URL附件
//            String[] fileListUrl = {"https://example.oss-cn-shanghai.aliyuncs.com/xxxxxxxxxxx1.png", "https://example.oss-cn-shanghai.aliyuncs.com/xxxxxxxxxxx2.png"};
//            for (String fileUrl : fileListUrl) {
//                String encodedUrl = URLEncoder.encode(fileUrl, "UTF-8").replace("%3A", ":").replace("%2F", "/");
//                MimeBodyPart mimeBodyPart = new MimeBodyPart();
//                mimeBodyPart.setDataHandler(new DataHandler(new URLDataSource(new URL(encodedUrl))));
//                // 提取文件名
//                String fileName = new URL(fileUrl).getPath();
//                fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
//                mimeBodyPart.setFileName(MimeUtility.encodeText(fileName));
//                multipart.addBodyPart(mimeBodyPart);
//            }

            // 添加完整消息
            message.setContent(multipart);
            // 发送附件代码,结束

            //mailSession.setDebug(true);//开启debug模式
            Transport.send(message);
            System.out.println("发送完成!");
        } catch (MessagingException | UnsupportedEncodingException e) {
            System.err.println("邮件发送失败: " + e.getMessage());
            e.printStackTrace();
//        } catch (MalformedURLException e) {
//            throw new RuntimeException(e);
        }

    }

}