Java Mail 类 API 的使用避坑指南

一、邮件协议

先来了解下邮件协议的作用主要是用于邮件的传输及接收,常用的协议有 SMTP (Simple Mail Transfer Protocol)、POP3 (Post Office Protocol version 3)、IMAP (Internet Message Access Protocol)

  • SMTP (Simple Mail Transfer Protocol)
    • 用途:SMTP 是用于发送电子邮件的主要协议。它定义了电子邮件在互联网上发送和转发的规则。
    • 工作方式:当用户通过电子邮件客户端发送邮件时,SMTP 协议负责将邮件从发送方的邮件服务器传输到接收方的邮件服务器。
  • POP3 (Post Office Protocol version 3)
    • 用途:POP3 是一种用于从邮件服务器接收电子邮件的协议。它允许用户下载邮件到本地设备上,通常用于桌面或移动设备。
    • 工作方式:用户使用 POP3 客户端连接到邮件服务器,下载邮件到本地,然后可以选择将邮件保留在服务器上或者删除
  • IMAP (Internet Message Access Protocol)
    • 用途:IMAP 也是一种用于从邮件服务器接收电子邮件的协议,但与 POP3 不同,IMAP 允许用户在多个设备上访问和管理邮件,而不需要下载到本地。
    • 工作方式:IMAP 客户端与邮件服务器保持连接,用户可以实时查看、搜索和管理邮件,而邮件仍然存储在服务器上。
协议 POP3(邮局协议版本3) IMAP(Internet邮件访问协议)
POP是一种简单的协议,仅允许将邮件从服务器下载到本地计算机。 IMAP更为先进,它使用户可以查看邮件服务器上的所有文件夹。
端口 POP服务器在端口110上侦听,而带SSL安全(POP3DS)服务器的POP在端口995上侦听 IMAP服务器侦听端口143,带有SSL安全(IMAPDS)服务器的IMAP侦听端口993。
访问方式 在POP3中,一次只能从单个设备访问邮件。 可以跨多个设备访问消息
要阅读邮件,必须将其下载到本地系统上。 在下载之前,可以部分读取邮件内容。
用户无法在邮件服务器的邮箱中整理邮件。 用户可以直接在邮件服务器上组织电子邮件。
用户无法在邮件服务器上创建,删除或重命名电子邮件。 用户可以在邮件服务器上创建,删除或重命名电子邮件。
用户在下载到本地系统之前无法搜索邮件的内容。 用户可以在下载前搜索邮件内容中的特定字符串。
下载后,如果本地系统崩溃消息丢失,则该消息存在于本地系统中。 邮件服务器上会保留邮件的多个冗余副本,如果丢失本地服务器的邮件,仍可以检索邮件
可以使用本地电子邮件软件更改邮件。 Web界面或电子邮件软件所做的更改与服务器保持同步。
所有消息立即下载。 可以在下载前查看邮件头。

二、协议之间需要注意的问题

2.1 删件

  • 使用 POP3 时需注意,当服务器设置了拉取后会删除时,会导致我们通过POP3拉取之后服务器会删除掉对应的邮件信息【这里需注意我这里使用时,并没有删除掉邮件,但是在邮箱的搜索处的确搜索不到了】

  • 使用 IMAP 时则不会出现删除的问题

2.2 拉取邮件

  • 使用 POP3 拉取邮件时,默认会当做所有邮件都在收件箱中,直接拉取收件箱即可拉取到所有邮件信息,并且可以使用 JavaMail API 中的条件过滤来对邮件进行筛选
  • 使用 IMAP 拉取邮件时,只会拉取到对应文件夹的邮件

三、拉取邮件附件时问题

3.1 附件拉取大文件再传输到其他系统

遇到直接使用 InputStream 直接传入其他系统不行问题

  • 思考一,本身拉取文件属于 SocketInputStream,是不是不可以直接传入

从URL上拉取文件属于 SocketInputStream【网络流】,是不是不可以直接传入,故把 Stream 中的数据全部 read() 到内存中再重新根据这个内容创建一个文件流来进行上传,实践出还是不行

  • 思考二,是不是文件太大导致下载问题所以一直传入其他系统是没有响应

这里使用小的附件的时候的确没问题,大的附件的确就会卡住,在这里卡了很久一度怀疑是这个问题,直到我一个操作发现不对劲,以下代码跟日志显示出,他每次在 socket 流上只会拉3次,即我创建文件缓冲区的3倍,打开了思路,他是不是每次拉的太小了?

  • 情况三,本身邮件拉取协议他本身也会有一个文件缓冲区,配置上叫做 fetchsize, 默认是 16kb, 这也解释了为什么他每次拉我设置本地缓冲区的三倍,可能还会有一些其他内容占用了剩下的 4KB,到这里即找到了确切的问题那么调大连接的缓冲区既可以解决
public Store createStore(EmailInfo emailInfo) throws Exception {
    // 邮箱连接信息
    String host = emailInfo.getHost();         	    // 邮箱服务器主机名
    String port = emailInfo.getPort();        	    // 邮箱服务器端口
    String userName = emailInfo.getUsername();   	// 邮箱用户名
    String password = emailInfo.getPassword();  	// 邮箱密码


    // 设置邮箱连接参数
    Properties props = new Properties();
    props.put("mail.store.protocol", "imaps");         // 设置协议为IMAP
    props.put("mail.imaps.host", host);                // 设置IMAP服务器主机名
    props.put("mail.imaps.port", port);                // 设置IMAP服务器端口
    props.put("mail.imaps.ssl.enable", "true");        // 设置是否使用SSL加密连接
    props.put("mail.imaps.fetchsize", "16777216");     // 设置文件缓冲区大小
    // 创建会话对象
    Session session = Session.getInstance(props, null);
    // 创建IMAP存储
    Store store = session.getStore("imaps");
    // 连接到邮件服务器
    store.connect(host, userName, password);

    // 判断是否连接成功
    if (store.isConnected()) {
        log.info("成功创建邮箱连接:" + userName);
        return store;
    } else {
        log.error("创建邮箱连接失败:" + userName);
        throw new Exception("store 为空");
    }
}

改完之后每次拉取的数量

mail.imaps.fetchsize 是 JavaMail API 中的一个系统属性,用于设置在使用 IMAP 协议通过 IMAPS(IMAP over SSL)连接到邮件服务器时,每次从服务器获取邮件内容的最大字节数。这个属性对于控制从邮件服务器下载邮件内容时的内存使用和网络流量非常有用。

当你设置 mail.imaps.fetchsize 属性时,你实际上是告诉 JavaMail API 每次从服务器请求的最大数据量。如果邮件的大小超过了这个限制,JavaMail 将会分多次请求数据,直到获取完整封邮件。

例如,如果你想将每次获取的最大字节数设置为 50 KB,你可以在代码中这样设置:

int fetchSize = 50 * 1024; // 50 KB
System.setProperty("mail.imaps.fetchsize", String.valueOf(fetchSize));

在调用 JavaMail API 来获取邮件之前设置这个属性,可以确保在获取邮件内容时遵守这个大小限制。

请注意,这个属性只影响从服务器获取邮件内容的过程,并不会影响其他类型的数据传输,比如邮件头部信息的获取。此外,不同的邮件服务器和网络环境可能对数据传输有不同的限制,因此合理设置 fetchsize 可以优化性能和资源使用。

如果你没有特别设置这个属性,JavaMail 将使用其默认值,这通常足够用于大多数情况。但是,在处理大型邮件或在带宽受限的环境中,调整这个值可能会有所帮助。

3.2 socketinputStream read() 的次数过多会产生什么影响

  1. CPU使用率增加:每次调用 read() 方法都可能涉及到CPU的处理,尤其是在循环中频繁调用时,这可能会导致CPU使用率的增加。
  2. 性能下降:如果每次 read() 调用读取的数据量很小,这可能会导致频繁的系统调用,从而降低整体性能。
  3. 延迟增加:每次 read() 调用都可能涉及到等待数据到达,如果数据到达的速度慢于 read() 调用的频率,这可能会导致延迟的增加。
  4. I/O操作频繁:频繁的I/O操作可能会导致I/O瓶颈,尤其是在高负载或多任务环境中。
  5. 内存使用:虽然每次 read() 调用可能只读取少量数据,但频繁调用可能会增加内存分配和回收的频率,从而影响内存使用效率。

所以我们要根据实际的文件需求来对缓冲区大小进行设置!

posted @ 2024-06-21 17:06  我来写笔记  阅读(458)  评论(0)    收藏  举报