javamail - 拉取邮件

一. 说明

演示版本 :Spring Boot - 2.4.1

二. 添加 Maven 依赖

<!-- 邮件服务 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
    <version>2.3.7.RELEASE</version>
</dependency>

三. 创建枚举

3.1 文件夹标识枚举

@Getter
@AllArgsConstructor
public enum FolderTypeEnums {
    INBOX(1, "INBOX", "收件箱"),
    Sent_Items(2, "Sent", "已发送邮件"),

    ;
    /**
     * 文件夹标记(入库使用)
     */
    private Integer code;
    /**
     * 文件夹英文标识(用来拉取时查询)
     */
    private String sign;
    /**
     * 文件夹描述
     */
    private String folderName;
}

3.2 邮箱Host枚举

@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum MailTypeEnums {
    QQ("qq", "imap.qq.com"),
    OUT_LOOK("outlook", "outlook.office365.com"),
    WANG_YI("163", "imap.163.com"),
    SINA("sohu", "imap.sohu.com"),
    SOHU("sina", "imap.sina.com"),

    ;
    private String mailType;
    private String host;

    /**
     * 根据类型取邮箱Host
     *
     * @param mailType 邮箱类型
     * @return java.lang.String
     */
    public static String getHostByType(String mailType){
        for (MailTypeEnums enums :MailTypeEnums.values()) {
            if (enums.getMailType().equals(mailType)) {
                return enums.getHost();
            }
        }
        return "";
    }
}

四. 添加解析邮件工具类

@Log4j2
public class AnalysisEmailUtil {
    /**
     * 获得邮件文本内容
     *
     * @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.delete(0,content.length());
            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);
                content.delete(0,content.length());
                getMailTextContent(bodyPart, content);
            }
        }
    }
}

五. 创建邮件常量类

public class MailConfigConstants {
    /**
     * 协议类型
     */
    public static final String MAIL_STORE_TYPE = "imap";
    /**
     * 端口
     */
    public static final String PORT = "993";
    /**
     * 端口
     */
    public static final String PROTOCOL = "imaps";
}

六. 创建Service层

6.1 拉取客户端链接

    public static Store getPullClient(String mailType, String email, String password) {
        /* 配置连接信息 */
        Properties properties = new Properties();
        // 协议类型
        properties.put("mail.store.protocol", MailConfigConstants.MAIL_STORE_TYPE);
        // 端口号
        properties.put("mail.imap.port", MailConfigConstants.PORT);
        // 邮箱Host
        properties.put("mail.imap.host", MailTypeEnums.getHostByType(mailType));
        // 配置 SMTP 连接属性
        properties.put("mail.imap.starttls.enable", "true");

        // 获取会话连接,单独使用拉取时使用getDefaultInstance,否则需要使用getInstance
        Session emailSession = Session.getInstance(properties);
        // 开发过程可开启日志辅助调试
//        emailSession.setDebug(true);
        // 获取Store对象
        Store store = null;
        try {
            store = emailSession.getStore(MailConfigConstants.PROTOCOL);
            // 登陆认证
            store.connect(MailTypeEnums.getHostByType(mailType), email, password);
        } catch (Exception e) {
            return null;
        }
        return store;
    }

6.2 拉取邮件

    /**
     * 拉取邮件
     *
     * @param mailType 邮箱类型, email 邮箱, password 密码, folderName 文件夹名
     */
    public void pullMailByFolder(String mailType, String email, String password, String folderName) {
        // 获取链接
        Store store = getPullClient(mailType, email, password);

        /* 设置文件夹参数 */
        Folder folderMail = null;
        try {
            folderMail = store.getFolder(folderName);
            // 设置对邮件帐户的访问权限,Folder.READ_ONLY:只读,Folder.READ_WRITE:读写(可以修改邮件的状态)
            folderMail.open(Folder.READ_WRITE);
        } catch (MessagingException e) {
            e.printStackTrace();
        }

        /* 获取邮件 */
        try {
            assert folderMail != null;
            // 获取邮件
            Message[] messages = folderMail.getMessages();
            log.info(email + "-> 获取 Message 数量:" + messages.length);

            // 获取&转换数据类型
            for (Message mail : messages) {
                // 主题
                System.out.println("主题:" + mail.getSubject());
                // 发送人信息
                InternetAddress addr = (InternetAddress) mail.getFrom()[0];
                System.out.println("发件人邮箱:" + addr.getAddress());
                System.out.println("发件人名称:" + addr.getPersonal());
                // 正文
                StringBuffer content = new StringBuffer(30);
                AnalysisEmailUtil.getMailTextContent(mail, content);
                System.out.println("正文:" + content.toString());
                // 发件时间
                System.out.println("发件时间:" + mail.getSentDate());
                // 收件时间
                System.out.println("收件时间:" + mail.getReceivedDate());
                // 正文类型
                System.out.println("正文类型:" + mail.getContentType());
                // 收件人信息
                InternetAddress reply = (InternetAddress)mail.getReplyTo()[0];
                System.out.println("收件人邮箱:" + reply.getAddress());
                System.out.println("收件人名称:" + reply.getPersonal());
                //
                System.out.println("???MessageNumber:"+mail.getMessageNumber());
            }

            folderMail.close(true);
            store.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

七. 创建 PullMailController 工具类

说明:参数“mailType”根据 MailTypeEnums 枚举类中的 mailType 参数和实际邮箱类型输入

@RestController
public class PullMailController {
    @Resource
    PullMailServiceImpl pullMailService;

    @GetMapping("pullMailByFolder")
    public void pullMailByFolder(String mailType, String email, String password) {
        System.out.println("开启子线程拉取");

        // 创建固定线程池,包含2个线程对象
        ExecutorService service = Executors.newFixedThreadPool(2);
        // 同时拉取收件箱和发件箱
        for (FolderTypeEnums f : FolderTypeEnums.values()) {
            // 获取链接对象时根据 mailType 获取host地址
            service.submit(() -> pullMailService.pullMailByFolder(mailType, email, password, f.getSign()));
        }
        System.out.println("已成功启动拉取");
    }
}

注意:

如果长时间拉取可能会发生文件夹关闭导致无法获取邮件信息,需要加校验

if (!folderMail.isOpen()) {
    folderMail.open(Folder.READ_WRITE);
}