通过JavaMail下载邮件附件

  • 需求
    • JavaMail
      • 工具类
      • 附件类
      • 使用
      • 遇到的一些意外

需求

因为某些特定的业务需要,要从邮件中获取附件。花了一些时间,最终业务需求实现了,但是也遇到一些坑,所以记录一下。

JavaMail

使用JavaMail来实现邮件的一系列操作(发送、接收),我使用的版本是1.6.2(maven仓库里有),不多说,上代码

工具类

package com.xxx.xxx;import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import com.sun.mail.pop3.POP3Folder;
import com.sun.mail.pop3.POP3Store;
import org.castor.util.Base64Decoder;import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.search.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.Security;
import java.util.Date;
import java.util.List;
import java.util.Properties;/*** @author jiangyw* @date 2019/1/7 19:29* <p>* 处理邮件工具类* 接收/发送服务器根据不同邮箱有不同的地址* 在邮箱设置里都可找到*/
public class MailUtil {// 发送服务器private static final String SEND_SMTP_HOST = "smtp.exmail.qq.com";private static final int SEND_SMTP_PORT = 465;// 接收服务器private static final String RECEIVE_IMAP_HOST = "imap.exmail.qq.com";private static final int RECEIVE_IMAP_PORT = 993;private static final String RECEIVE_POP_HOST = "pop.exmail.qq.com";private static final int RECEIVE_POP_PORT = 110;// 账号private String user;// 密码private String password;private IMAPFolder imapFolder = null;private IMAPStore imapStore = null;private POP3Store pop3Store = null;private POP3Folder pop3Folder = null;public MailUtil() {this("your email address", "your email password");}private MailUtil(String user, String password) {this.user = user;this.password = password;}/*** 发送邮件** @param toEmail 收件人邮箱地址* @param subject 邮件标题* @param content 邮件内容 可以是html内容*/public void send(String toEmail, String subject, String content) {Session session = loadSendSession();// session.setDebug(true);// 创建邮件消息MimeMessage message = new MimeMessage(session);try {// 设置发件人message.setFrom(new InternetAddress(user));Address[] a = new Address[1];a[0] = new InternetAddress(user);message.setReplyTo(a);// 设置收件人InternetAddress to = new InternetAddress(toEmail);message.setRecipient(MimeMessage.RecipientType.TO, to);// 设置邮件标题message.setSubject(subject);// 设置邮件的内容体message.setContent(content, "text/html;charset=UTF-8");// 发送邮件Transport.send(message);} catch (MessagingException e) {e.printStackTrace();String err = e.getMessage();// 在这里处理message内容, 格式是固定的System.out.println(err);}}/*** 发送邮件 带附件** @param toEmail    收件人邮箱地址* @param subject    邮件标题* @param content    邮件内容 可以是html内容* @param attachPath 附件路径*/public void send(String toEmail, String subject, String content, String attachPath) {Session session = loadSendSession();MimeMessage mm = new MimeMessage(session);try {//发件人mm.setFrom(new InternetAddress(user));//收件人mm.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail)); // 设置收件人// mm.setRecipient(Message.RecipientType.CC, new// InternetAddress("XXXX@qq.com")); //设置抄送人//标题mm.setSubject(subject);//内容Multipart multipart = new MimeMultipart();//body部分BodyPart contentPart = new MimeBodyPart();contentPart.setContent(content, "text/html;charset=utf-8");multipart.addBodyPart(contentPart);//附件部分BodyPart attachPart = new MimeBodyPart();FileDataSource fileDataSource = new FileDataSource(attachPath);attachPart.setDataHandler(new DataHandler(fileDataSource));attachPart.setFileName(MimeUtility.encodeText(fileDataSource.getName()));multipart.addBodyPart(attachPart);mm.setContent(multipart);Transport.send(mm);} catch (Exception e) {e.printStackTrace();String err = e.getMessage();// 在这里处理message内容, 格式是固定的System.out.println(err);}}private Session loadSendSession() {try {// 配置发送邮件的环境属性final Properties props = new Properties();// 表示SMTP发送邮件,需要进行身份验证props.put("mail.smtp.auth", "true");props.put("mail.smtp.host", SEND_SMTP_HOST);// props.put("mail.smtp.port", SEND_SMTP_PORT);// 如果使用ssl,则去掉使用25端口的配置,进行如下配置,props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");props.put("mail.smtp.socketFactory.port", SEND_SMTP_PORT);props.put("mail.smtp.port", SEND_SMTP_PORT);// 发件人的账号props.put("mail.user", user);// 访问SMTP服务时需要提供的密码props.put("mail.password", password);// 构建授权信息,用于进行SMTP进行身份验证Authenticator authenticator = new Authenticator() {@Overrideprotected PasswordAuthentication getPasswordAuthentication() {// 用户名、密码String userName = props.getProperty("mail.user");String password = props.getProperty("mail.password");return new PasswordAuthentication(userName, password);}};// 使用环境属性和授权信息,创建邮件会话return Session.getInstance(props, authenticator);} catch (Exception e) {e.printStackTrace();System.out.println("mail session is null");}return null;}public void saveFile(List<MailAttachment> list) throws IOException {for (MailAttachment mailAttachment : list) {InputStream inputStream = mailAttachment.getInputStream();FileOutputStream outputStream = new FileOutputStream(new File("C:\\Users\\jiangyw\\Desktop\\test.pdf"));int len;byte[] bytes = new byte[1024];while ((len = inputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);outputStream.flush();}inputStream.close();outputStream.close();}}/*** 获取附件* 只获取附件里的* 邮件内容里的附件(图片等)忽略* @param part 邮件中多个组合体中的其中一个组合体* @param list 附件容器* @throws UnsupportedEncodingException* @throws MessagingException* @throws FileNotFoundException* @throws IOException*/public void getAttachment(Part part, List<MailAttachment> list) throws UnsupportedEncodingException, MessagingException,FileNotFoundException, IOException {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();// 附件名通过MimeUtility解码,否则是乱码String name = MimeUtility.decodeText(bodyPart.getFileName());list.add(new MailAttachment(name, is));} else if (bodyPart.isMimeType("multipart/*")) {getAttachment(bodyPart, list);} else {String contentType = bodyPart.getContentType();if (contentType.contains("name") || contentType.contains("application")) {}}}} else if (part.isMimeType("message/rfc822")) {getAttachment((Part) part.getContent(), list);}}/*** 判断邮件中是否包含附件** @return 邮件中是否存在附件* @throws MessagingException* @throws IOException*/public boolean hasAttachment(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 = hasAttachment(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 = hasAttachment((Part) part.getContent());}return flag;}/*** 获得邮件文本内容** @param part        邮件体* @param textContent 存储邮件文本内容的字符串* @param htmlContent 存储邮件html内容的字符串* @throws MessagingException* @throws IOException*/public void getMailTextContent(Part part, StringBuffer textContent, StringBuffer htmlContent) throws MessagingException, IOException {//如果是文本类型的附件,通过getContent方法可以取到文本内容,但这不是我们需要的结果,所以在这里要做判断boolean isContainTextAttach = part.getContentType().indexOf("name") > 0;if (part.isMimeType("text/*") && !isContainTextAttach) {String contentType = part.getContentType();if (contentType.startsWith("TEXT/PLAIN") || contentType.startsWith("text/plain")) {textContent.append(part.getContent().toString());}if (contentType.startsWith("TEXT/HTML") || contentType.startsWith("text/html")) {htmlContent.append(part.getContent().toString());}} else if (part.isMimeType("message/rfc822")) {getMailTextContent((Part) part.getContent(), textContent, htmlContent);} 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, textContent, htmlContent);}}}public String getSubject(Message message) throws MessagingException {return message.getSubject();}/*** 获取邮件信息* IMAP协议* 下载附件的时候巨慢* 如果需要获取附件的时候不推荐使用* 该协议下只能查询receivedDate* sentDate都为null** @throws MessagingException*/public Message[] getImapMessages() throws MessagingException {String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());System.setProperty("mail.mime.splitlongparameters", "false");Properties props = System.getProperties();props.setProperty("mail.imap.socketFactory.class", SSL_FACTORY);props.setProperty("mail.imap.socketFactory.port", "993");props.setProperty("mail.imapStore.protocol", "imap");props.setProperty("mail.imap.host", RECEIVE_IMAP_HOST);props.setProperty("mail.imap.port", "993");props.setProperty("mail.imap.auth.login.disable", "true");Session session = Session.getDefaultInstance(props, null);session.setDebug(false);imapStore = (IMAPStore) session.getStore("imap");  // 使用imap会话机制,连接服务器imapStore.connect(RECEIVE_IMAP_HOST, RECEIVE_IMAP_PORT, user, password);imapFolder = (IMAPFolder) imapStore.getFolder("INBOX"); //收件箱imapFolder.open(Folder.READ_WRITE);// 获取前一天的邮件信息SearchTerm endTerm = new ReceivedDateTerm(ComparisonTerm.LE, new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000L));SearchTerm startTerm = new ReceivedDateTerm(ComparisonTerm.GE, new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000L));SearchTerm searchTerm = new AndTerm(startTerm, endTerm);return imapFolder.search(searchTerm);}/*** 通过pop3协议获取邮件信息* pop3协议下只能通过sentDate来查询* receivedDate都为null** @return Message[]* @throws MessagingException*/public Message[] getPopMessages() throws MessagingException {Properties props = new Properties();props.setProperty("mail.imapStore.protocol", "pop3");       // 使用pop3协议props.setProperty("mail.pop3.port", "110");           // 端口props.setProperty("mail.pop3.host", RECEIVE_POP_HOST);       // pop3服务器Session session = Session.getInstance(props);pop3Store = (POP3Store) session.getStore("pop3");pop3Store.connect(RECEIVE_POP_HOST, RECEIVE_POP_PORT, user, password);// 获得收件箱pop3Folder = (POP3Folder) pop3Store.getFolder("INBOX");/* Folder.READ_ONLY:只读权限* Folder.READ_WRITE:可读可写(可以修改邮件的状态)*/pop3Folder.open(Folder.READ_ONLY); //打开收件箱// 获取前一天的邮件信息SearchTerm endTerm = new SentDateTerm(ComparisonTerm.LE, new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000L));SearchTerm startTerm = new SentDateTerm(ComparisonTerm.GE, new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000L));SearchTerm searchTerm = new AndTerm(startTerm, endTerm);
//         得到收件箱中的所有邮件,并解析return pop3Folder.search(searchTerm);}/*** 关闭folder和store资源** @throws MessagingException*/public void close() throws MessagingException {if (imapFolder != null) {imapFolder.close(false);}if (imapStore != null) {imapStore.close();}if (pop3Folder != null) {pop3Folder.close(false);}if (pop3Store != null) {pop3Store.close();}}}

附件类

很简单的容器类:

package com.xxx.xxx;import java.io.InputStream;/*** @author jiangyw* @date 2019/1/10 21:14** 邮件附件类*/
public class MailAttachment {private String name;private InputStream inputStream;public String getName() {return name;}public void setName(String name) {this.name = name;}public InputStream getInputStream() {return inputStream;}public void setInputStream(InputStream inputStream) {this.inputStream = inputStream;}public MailAttachment(String name, InputStream inputStream) {this.name = name;this.inputStream = inputStream;}
}

使用

public void readMail() throws MessagingException {long startTime = System.currentTimeMillis();MailUtil mailUtil = new MailUtil();Message[] messages = mailUtil.getPopMessages();for (Message message : messages) {String subject = mailUtil.getSubject(message);try {List<MailAttachment> mailAttachments = new ArrayList<>();mailUtil.getAttachment(message, mailAttachments);saveAttachment(mailAttachments);}} catch (Exception ignore) {}}mailUtil.close();System.out.println("解析邮件共耗时:" + (System.currentTimeMillis() - startTime) + "毫秒");}private void saveAttachment(List<MailAttachment> list) throws IOException {for (MailAttachment mailAttachment : list) {InputStream inputStream = mailAttachment.getInputStream();FileOutputStream outputStream = new FileOutputStream(new File( "your path");int len;byte[] bytes = new byte[1024 * 1024];while ((len = inputStream.read(bytes)) != -1) {outputStream.write(bytes, 0, len);outputStream.flush();}inputStream.close();outputStream.close();}}

遇到的一些意外

  1. 下载附件速度慢到令人发指!!!(看图)

    两个一共不到4M的附件,下载总共用了1000多秒!!!
    解决方法:不要用IMAP协议接收邮件信息,用POP3协议,换了协议之后的速度(看图)

    速度差了两个数量级!!!
  2. 在代码里也注释了,用IMAP协议时没有SentDate,用POP3协议时没有ReceivedDate;这两个协议具体的区别百度一下你就知道,我是因为要获取前一天的邮件,所以是根据日期来搜索。
  3. 附件名乱码,这个其实很简单,用JavaMail自带的MimeUtility解码一下就好。

通过JavaMail下载邮件附件相关推荐

  1. outlook搜索栏跑到上面去了_Outlook邮箱批量下载邮件附件+快速复制文件名

    今天的文章很简单,但很实用,可用于批量接收并下载别人发给你的邮件附件. 背景 作为老师,每年都会收到许多必须要通过附件接收的学生邮件,但学生多了.作业多了,老师下载学生附件的操作就变得极为枯燥和浪费时 ...

  2. python自动下载邮件附件_Python邮件处理(收取、解码及附件下载)

    之前在工作中需要用到自动查收邮件和下载附件,于是用python做了一些自动化的尝试.以下代码主要实现了两个功能,一个是下载并解码邮件正文(print_info函数),另一个是下载邮件附件(get_at ...

  3. Java收取腾讯企业邮件和下载邮件附件

    1.使用前请先查看下面图片进行邮箱配置 2.Demo轻量级,只需要引入项目中lib文件夹的mail-1.4.7.jar包即可; 3.最后main方法执行: 密码设置: 收取邮件数量设置: Demo源码 ...

  4. python自动下载邮件附件_Python批量下载电子邮件附件并汇总合并Excel文件

    原标题:Python批量下载电子邮件附件并汇总合并Excel文件 前几天在公众号搞了一波送书活动,详见福利:免费赠送240本Python教材,该文推送之后,立刻收到了大量的样书申请表,那么接下来的工作 ...

  5. python+POP3 批量下载邮件附件

    最近新开学,接到了给老板的本科课程当助教的工作,百十来号人一学期下来得有四五次作业发进邮箱里,需要我来统计打分,想想挨个点进去下载附件的过程就头大,于是萌生了写个脚本来统计作业的想法. 其实pytho ...

  6. python自动下载邮件附件_Python 批量导出邮件附件 | 互联网笔记

    采用python 3 windows 环境可Anaconda进行一键安装环境, 此脚本适用用于下列状况 报表每日自动发送至邮箱,多邮箱每天需要登陆或使用客户端下载邮件 收集的资料手动下载太麻烦. 可在 ...

  7. python批量下载邮件附件

    背景 由于同学每周要通过邮箱收数学建模作业,100多人给她发附件,她要一个个地点着下载. 太麻烦了,所以想用程序实现下载附件的功能. 在网上查资料后,最终实现了稍为简单的下载附件功能,代码有些细节还不 ...

  8. python 登陆邮箱下载邮件附件

    参考帖子:https://blog.csdn.net/u012209894/article/details/82384987 最近有一个比较特殊的需求,有一个业务每日会产生大量报表,该业务并未给出拿取 ...

  9. python3下载邮件附件

    python3下载.eml文件的附件 最近经常干的一件事就是面对几十个的.eml文件,逐个打开并下载附件,今天突发奇想,人生苦短,快用python,那么为啥这种重复性的工作不用python替代呢,说干 ...

最新文章

  1. 12种Numpy Pandas高效技巧
  2. linux gcc-9.2.0 源码编译
  3. linux富文本软件,CherryTree For Linux
  4. python导入模块报错_Python 导入上层目录模块报错
  5. php的实体字符表,php htmlspecialchars_decode() 函数把预定义的 HTML 实体转换为字符
  6. 一个测试新人的职业规划——三个月
  7. Java的接口及实例(转)
  8. 让博客园博客自动生成章节目录索引
  9. 千峰python资料下载_千锋Python基础视频教程全集下载
  10. List集合去重的三种方法
  11. 南开计算机等级,南开100题分类-全国计算机等级考试上机考试习题集(二级C)(南开大学出版社)...
  12. python wmi 显卡型号_确定通过WMI运行的GPU
  13. ffmpeg给视频加水印
  14. Android性能测试文章汇总
  15. 英飞凌AURIX HSM介绍
  16. 为什么设计思维对产品设计有帮助?
  17. 软件测试之bug管理
  18. 网络经济与企业管理【十一】之企业文化管理
  19. 几种同源关系:直系同源、旁系同源和异同源
  20. 浏览器原理及性能优化

热门文章

  1. Every-SG游戏
  2. Nim游戏和SG函数
  3. socket编程(在线自动聊天工具) --Python3
  4. 狄利克雷分布公式_关于狄利克雷分布的理解
  5. k8s报错503或者其他网络错误 Readiness probe failed: HTTP probe failed with statuscode: 503
  6. 【STM32利用CuBe MX生成HID设备】1-熟悉软件以及生成一个8键的游戏控制器
  7. 计算机组成原理-I/O系统
  8. windows7设置通电自启动_怎么让Windows7系统电脑自动开机?
  9. 碳纳米管(CNT)的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
  10. ROS从入门到精通系列(十三)-- PR2移动双臂机器人基础