DOIFOR技术Java使用POP3收取邮件
DOIFOR技术Java使用POP3收取邮件

Java使用POP3收取邮件

技术

简单示例

首先需要引入需要的依赖,因为我的项目是基于Springboot做的,所以直接引入spring-mail即可:

implementation 'org.springframework.boot:spring-boot-starter-mail'

该starter中包含了java-mail的完整依赖:

image

创建session,需要找到邮箱服务器的pop地址、端口、是否使用ssl,找到后定义property:

private static final Properties PROPERTIES = new Properties();
PROPERTIES.put("mail.store.protocol", "pop3");
PROPERTIES.put("mail.pop3.host", HOST);
PROPERTIES.put("mail.pop3.port", "995");
// 以下是使用SSL时需要的设置
PROPERTIES.put("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
PROPERTIES.put("mail.pop3.socketFactory.fallback", "false");
Session session = Session.getDefaultInstance(PROPERTIES);

打开收件箱,Store对象实现了AutoCloseable接口,可以使用try-resource语法简化代码。 pop协议只能打开INDEX,因此代码如下:

try (Store store = session.getStore("pop3")) {
    // username就是邮箱,password是邮箱密码或者授权码
    store.connect(HOST, USERNAME, PASSWORD);
    Folder inbox = store.getFolder("INBOX");
} catch (Exception e) {
    log.error(e.getMessage(), e);
}

遍历邮件,需要先打开文件夹,然后获取邮件列表,pop协议每次获取都是获取服务器上的全部邮件,在实际使用中需要考虑这个问题:

inbox.open(Folder.READ_ONLY);
Message[] messages = inbox.getMessages();
for (Message message : messages) {
    System.out.println(message.getSubject())
}

至此获取就已经能够完成基本的邮件收取工作了。

解析邮件

主要参考java之邮件读取和解析_java读取邮件内容-CSDN博客[^1]。

需要考虑附件存储、内容读取、收件人等

import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMultipart;
import jakarta.mail.internet.MimeUtility;
import org.eclipse.angus.mail.pop3.POP3Message;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class POP3MessageWrapper {
    private final POP3Message message;

    public POP3MessageWrapper(POP3Message message) {
        this.message = message;
    }

    public String getId() throws MessagingException {
        return message.getMessageID();
    }

    public String getSubject() throws MessagingException, UnsupportedEncodingException {
        if (message.getSubject() == null) {
            return "";
        }
        return MimeUtility.decodeText(message.getSubject());
    }

    public String getFrom() throws MessagingException, UnsupportedEncodingException {
        StringBuilder builder = new StringBuilder();
        Address[] froms = message.getFrom();
        if (froms.length < 1) { throw new MessagingException("没有发件人!"); }

        InternetAddress address = (InternetAddress) froms[0];
        String person = address.getPersonal();
        if (person != null) {
            person = MimeUtility.decodeText(person) + " ";
        } else {
            person = "";
        }
        builder.append(person).append('<').append(address.getAddress()).append('>');
        return builder.toString();
    }

    public String getReceiveAddress() throws MessagingException {
        StringBuilder receiveAddress = new StringBuilder();
        Address[] add = message.getAllRecipients();
        if (add == null || add.length < 1) { throw new MessagingException("没有收件人!"); }
        for (Address address : add) {
            InternetAddress internetAddress = (InternetAddress) address;
            receiveAddress.append(internetAddress.toUnicodeString()).append(",");
        }
        //删除最后一个逗号
        receiveAddress.deleteCharAt(receiveAddress.length() - 1);

        return receiveAddress.toString();
    }

    public Date getSentDate() throws MessagingException {
        return  message.getSentDate();
    }

    public boolean isContainAttachment() throws MessagingException, IOException {
        return isContainAttachment(message);
    }

    public boolean isSeen() throws MessagingException {
        return message.getFlags().contains(Flags.Flag.SEEN);
    }

    public boolean isReplySign() throws MessagingException {
        boolean replySign = false;
        String[] headers = message.getHeader("Disposition-Notification-To");
        if (headers != null) { replySign = true; }
        return replySign;
    }

    public String getPriority() throws MessagingException {
        String priority = "普通";
        String[] headers = message.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;
    }

    public String getContent() throws MessagingException, IOException {
        StringBuilder builder = new StringBuilder();
        getMailTextContent(message, builder);
        return builder.toString();
    }

    public List getAttachments() throws MessagingException, IOException {
        return saveAttachments(message);
    }

    private List saveAttachments(Part part) throws MessagingException, IOException {
        List result = new ArrayList<>();
        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 disposition = bodyPart.getDisposition();
                if (disposition != null && (disposition.equalsIgnoreCase(Part.ATTACHMENT) || disposition.equalsIgnoreCase(Part.INLINE))) {
                    InputStream is = bodyPart.getInputStream();
                    result.add(saveFile(is, getSubject()+"/"+bodyPart.getFileName()));
                } else if (bodyPart.isMimeType("multipart/*")) {
                    result.addAll(saveAttachments(bodyPart));
                } else {
                    String contentType = bodyPart.getContentType();
                    if (contentType.contains("name") || contentType.contains("application")) {
                        result.add(saveFile(bodyPart.getInputStream(), getSubject()+"/"+bodyPart.getFileName()));
                    }
                }
            }
        } else if (part.isMimeType("message/rfc822")) {
            result.addAll(saveAttachments((Part) part.getContent()));
        }
        return result;
    }

    private String saveFile(InputStream inputStream, String path) {
        //fixme: 完成具体的存储工作,本地存储或者nas等
        return null;
    }

    private void getMailTextContent(Part part, StringBuilder 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);
            }
        }
    }

    private 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;
    }
}

删除已读文件

pop协议默认是不删除服务器上的邮件的,如果确实需要删除,需要在读取邮件后,在关闭连接之前发送删除指令,大概方法如下:

try (Store store = session.getStore("pop3")) {
    store.connect(HOST, USERNAME, PASSWORD);
    Folder inbox = store.getFolder("INBOX");
    inbox.open(Folder.READ_ONLY);
    Message[] messages = inbox.getMessages();
    for (Message message : messages) {
        System.out.println(message.getSubject())
        // 标记删除
        message.setFlag(Flags.Flag.DELETED, true);
    }
    // 关闭文件夹,参数为true时会删除标记删除的邮件
    inbox.close(true);
} catch (Exception e) {
    log.error(e.getMessage(), e);
}

本文基本上将java-mail使用pop3收取邮件的场景覆盖,后续遇到了再补充上来吧。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注