项目场景:
项目场景:
通过定时任务调用解析邮件,获取附件,上传到文件服务器(协助客户上传文件),将邮件移动到相应的文件夹。以下是简单的邮件操作逻辑
本地demo代码贴贴:
由于业务场景需要,这里使用的是IMAP协议,java mail 版本是1.6.2,使用腾讯企业邮箱
package com.didadiandi.email;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import javax.mail.Authenticator;
import javax.mail.BodyPart;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* TODO
*
* @author wj
* @date 2021/9/18 10:56
*
*/
public class EmailTest {
public static void main(String[] args) throws Exception {
try (IMAPStore store = connectStore()) {
Map<String, IMAPFolder> folderMap = folderMap(store);
try (IMAPFolder newComingFolder = folderMap.get("NEW COMING");
IMAPFolder autoUploadedFolder = folderMap.get("AUTO UPLOADED");
IMAPFolder pendingCheckFolder = folderMap.get("PENDING CHECK");
IMAPFolder notMatchedFolder = folderMap.get("NOT MATCHED");) {
Message[] messages = newComingFolder.getMessages();
Map<String, MimeMessage> mimeMessageMap = new HashMap<>();
for (Message message : messages) {
MimeMessage mimeMessage = (MimeMessage) message;
if (mimeMessage == null) {
continue;
}
String messageId = mimeMessage.getMessageID();
mimeMessageMap.put(messageId, mimeMessage);
}
System.out.println(mimeMessageMap.size());
for (Map.Entry<String, MimeMessage> mimeMessageEntry : mimeMessageMap.entrySet()) {
MimeMessage message = mimeMessageEntry.getValue();
System.out.println("\n");
System.out.println(" subject:" + message.getSubject());
if (!isContainAttachment(message)) {
continue;
}
saveAttachment(message);
moveMessage(newComingFolder, autoUploadedFolder, message);
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 设置配置,且连接邮箱
*
* @return com.sun.mail.imap.IMAPStore
* @author wj
* @date 15:16 2022/1/6
*/
public static IMAPStore connectStore() throws MessagingException {
Properties props = new Properties();
// 协议
props.put("mail.store.protocol", "imap");
// 要连接的imap服务器
props.put("mail.imap.host", "imap.exmail.qq.com");
// 端口
props.put("mail.imap.port", "993");
// 是否使用ssl连接并使用ssl端口(imap协议默认为false)
props.put("mail.imap.ssl.enable", true);
// 设置超时时间(默认无超时时间)
props.put("mail.imap.timeout", "30000");
// 连接超时时间(默认无超时时间)
props.put("mail.imap.connectiontimeout", "30000");
// 缓存消息的最大大小
props.put("mail.imap.appendbuffersize", "6291456");
// 是否开启分段加载功能(默认为true),附件大于100K需要加这个配置,能加快读取附件的时间
props.put("mail.imap.partialfetch", "false");
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("userName", "PASSWORD");
}
});
Store store = session.getStore();
store.connect();
return (IMAPStore) store;
}
/**
* 获取邮箱
*
* @return java.util.Map<java.lang.String,com.sun.mail.imap.IMAPFolder>
* @author wj
* @date 15:17 2022/1/6
*/
public static Map<String, IMAPFolder> folderMap(IMAPStore store) throws MessagingException {
Map<String, IMAPFolder> folderMap = new HashMap<>();
// 带附件的邮箱文件夹
IMAPFolder attachmentFolder = (IMAPFolder) openFolder(store, "NEW COMING", false);
folderMap.put("NEW COMING", attachmentFolder);
// 未匹配文件夹
IMAPFolder unmatchedFolder = (IMAPFolder) openFolder(store, "NOT MATCHED", false);
folderMap.put("NOT MATCHED", unmatchedFolder);
// 上传失败文件夹
IMAPFolder uploadFailedFolder = (IMAPFolder) openFolder(store, "PENDING CHECK", false);
folderMap.put("PENDING CHECK", uploadFailedFolder);
// 上传成功文件夹
IMAPFolder uploadSuccessFolder = (IMAPFolder) openFolder(store, "AUTO UPLOADED", false);
folderMap.put("AUTO UPLOADED", uploadSuccessFolder);
return folderMap;
}
/**
* 判断是否存在附件
*
* @return boolean
* @author wj
* @date 15:17 2022/1/6
*/
public static boolean isContainAttachment(Part part) throws MessagingException, IOException, InterruptedException {
// 是否包含multipart内容
if (part.isMimeType("multipart/*")) {
// 转换content为MimeMultipart对象
MimeMultipart multipart = (MimeMultipart) part.getContent();
// 获取bodyPart的数量
int partCount = multipart.getCount();
for (int i = 0; i < partCount; i++) {
BodyPart bodyPart = multipart.getBodyPart(i);
// 返回bodyPart的配置
String disp = bodyPart.getDisposition();
// 是否包含附件
if (disp != null && (disp.equalsIgnoreCase(Part.ATTACHMENT) || disp.equalsIgnoreCase(Part.INLINE))) {
return true;
}
}
}
return false;
}
/**
* 保存附件
*
* @param part 邮件中多个组合体中的其中一个组合体
* @throws UnsupportedEncodingException
* @throws MessagingException
* @throws IOException
*/
private static void saveAttachment(Part part) throws UnsupportedEncodingException, MessagingException,
IOException {
System.out.println(" hashCode :" + part.hashCode());
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 disp = bodyPart.getDisposition();
if (disp != null) {
String fileName = decodeText(bodyPart.getFileName());
System.out.println(" fileName :" + fileName);
// 通过getInputStream方法下载附件
// bodyPart.getInputStream();
}
}
}
}
/**
* 获取邮箱
*
* @return javax.mail.Folder
* @author wj
* @date 15:19 2022/1/6
*/
public static Folder openFolder(Store store, String folderName, boolean isDefaultFolder) throws MessagingException {
Folder folder;
try {
// 获取收件箱文件夹
if (isDefaultFolder) {
folder = store.getFolder(folderName);
} else {
// 自定义邮箱文件夹
Folder[] folders = store.getDefaultFolder().list();
folder = findFolderByName(folders, folderName);
}
folder.open(Folder.READ_WRITE);
return folder;
} catch (MessagingException e) {
store.close();
e.printStackTrace();
}
return null;
}
/**
* 寻找邮箱文件夹
*
* @return javax.mail.Folder
* @author wj
* @date 15:00 2021/9/15
*/
public static Folder findFolderByName(Folder[] sourceFolder, String folderName) throws MessagingException {
for (Folder folder : sourceFolder) {
if (folder.getName().equals(folderName)) {
return folder;
} else if (folder.list().length > 0) {
return findFolderByName(folder.list(), folderName);
}
}
return null;
}
/**
* 解析文件名称
*
* @return java.lang.String
* @author wj
* @date 15:20 2022/1/6
*/
private static String decodeText(String encodeText) throws UnsupportedEncodingException {
if (encodeText == null || "".equals(encodeText)) {
return "";
} else {
return MimeUtility.decodeText(encodeText).replaceAll("/", "_");
}
}
/**
* 移动邮件
*
* @return void
* @author wj
* @date 15:21 2022/1/6
*/
public static void moveMessage(IMAPFolder sourceFolder, IMAPFolder targetFolder, Message messages) throws MessagingException {
if (messages == null) {
return;
}
try {
sourceFolder.moveMessages(new Message[]{messages}, targetFolder);
} catch (Exception e) {
throw e;
}
}
}
问题描述:
遇到的问题:附件获取重复(测试的邮件附件名称都不一样)
// BUG原因是,for循环中调用该方法 moveMessage(newComingFolder, autoUploadedFolder, message);
/**
* 移动邮件
*
* @return void
* @author wj
* @date 15:21 2022/1/6
*/
public static void moveMessage(IMAPFolder sourceFolder, IMAPFolder targetFolder, Message messages) throws MessagingException {
if (messages == null) {
return;
}
try {
sourceFolder.moveMessages(new Message[]{messages}, targetFolder);
} catch (Exception e) {
throw e;
}
}
原因分析:
有两个可能:第一个可能是java mail 本身存在问题;第二个可能是腾讯企业邮箱存在问题,不支持这样操作
解决方案:
将其批量走完业务之后,再一次性调用移动邮件方法。代码如下: public static void main(String[] args) throws Exception {
try (IMAPStore store = connectStore()) {
Map<String, IMAPFolder> folderMap = folderMap(store);
try (IMAPFolder newComingFolder = folderMap.get("NEW COMING");
IMAPFolder autoUploadedFolder = folderMap.get("AUTO UPLOADED");
IMAPFolder pendingCheckFolder = folderMap.get("PENDING CHECK");
IMAPFolder notMatchedFolder = folderMap.get("NOT MATCHED");) {
Message[] messages = newComingFolder.getMessages();
Map<String, MimeMessage> mimeMessageMap = new HashMap<>();
for (Message message : messages) {
MimeMessage mimeMessage = (MimeMessage) message;
if (mimeMessage == null) {
continue;
}
String messageId = mimeMessage.getMessageID();
mimeMessageMap.put(messageId, mimeMessage);
}
// 新增邮件处理后保存的集合
List<MimeMessage> autoUploadedMessageList = Lists.newArrayList();
System.out.println(mimeMessageMap.size());
for (Map.Entry<String, MimeMessage> mimeMessageEntry : mimeMessageMap.entrySet()) {
MimeMessage message = mimeMessageEntry.getValue();
System.out.println("\n");
System.out.println(" subject:" + message.getSubject());
if (!isContainAttachment(message)) {
continue;
}
saveAttachment(message);
// 根据业务需求,加入集合中
autoUploadedMessageList.add(message);
}
// 这批邮件都执行完毕之后,再移动邮件
moveMessage(newComingFolder, autoUploadedFolder, autoUploadedMessageList);
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 移动邮件(调整过后)
*
* @return void
* @author wj
* @date 15:21 2022/1/6
*/
public static void moveMessage(IMAPFolder sourceFolder, IMAPFolder targetFolder, List<MimeMessage> messages) throws MessagingException {
if (messages == null) {
return;
}
try {
sourceFolder.moveMessages(messages.toArray(new Message[0]), targetFolder);
} catch (Exception e) {
throw e;
}
}
// 也可以这样调整,但是经过评估还是使用的上面那个方法。这个方法可以在循环中调用(处理完一封邮件就"移动一封")
public static void moveMessage(IMAPFolder sourceFolder, IMAPFolder targetFolder,MimeMessage messages) throws MessagingException {
if (messages == null) {
return;
}
try {
sourceFolder.copyMessages(new Message[]{messages}, targetFolder);
messages.setFlag(Flags.Flag.DELETED, true);
} catch (Exception e) {
throw e;
}
}