This commit is contained in:
oneao committed 2025-07-14 17:32:25 +08:00
1 parent 6c057cbdca
commit 34deecb157
3 files changed
+530 -58

No files matched your search

@@ -2,6 +2,7 @@ package com.email;
import com.email.core.fetch.EmailFetcher;
import com.email.core.fetch.ImapJavaMailEmailFetcher;
import com.email.core.fetch.model.EmailFetchData;
import com.email.domain.entity.UserAccount;
import lombok.extern.slf4j.Slf4j;
@@ -31,9 +32,13 @@ public class Test {
},
// folderConsumer: 开始处理某个文件夹
folder -> {
log.info("folder:{}",folder.getName());
},
// fetchDataConsumer: 每封邮件拉取完成后处理
emailData -> {
for (EmailFetchData emailDatum : emailData) {
log.info("subject:{}",emailDatum.getEmailSummary().getSubject());
}
}
);
}
@@ -0,0 +1,196 @@
package com.email.core;
import com.email.prop.EmailStorageProperties;
import com.email.utils.entity.EmailUploadResult;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadLocalRandom;
public class EmailFileUtils {
private static final ExecutorService ATTACHMENT_EXECUTOR =
Executors.newFixedThreadPool(4); // 或使用 ThreadPoolExecutor 自定义
private static String LOCAL_BASE_PATH; // e.g. D:/data/email
private static String LOCAL_SERVER_URL; // e.g. http://127.0.0.1:8081
private static String LOCAL_STATIC_URL; // e.g. /static/email
private static final String BODY_PREFIX = "Body"; // 正文
private static final String ATTACHMENT_PREFIX = "Attachment"; // 附件以及内联资源
private static final String TEMPLATE_PREFIX = "Template"; // 模板
public static void init(EmailStorageProperties properties) {
LOCAL_BASE_PATH = properties.getBasePath();
LOCAL_SERVER_URL = properties.getServerUrl();
LOCAL_STATIC_URL = properties.getStaticPath();
}
/**
* 规范化邮件在存储时候的格式,将 @ 格式化为 _
* @param email 邮件地址
* @return 格式化后的邮件地址
*/
private static String normalizeEmail(String email) {
if (email == null) return "unknown";
return email.replace("@", "_").replace(".", "_");
}
/**
* 通过文件名称后去文件的后缀
* @param fileName 文件名称
* @return 文件后缀
*/
private static String getFileExtension(String fileName) {
int idx = fileName.lastIndexOf('.');
return (idx != -1 && idx < fileName.length() - 1) ? fileName.substring(idx + 1) : "";
}
/**
* 保存邮件内容
* @param email 邮件地址
* @param bodyContent 邮件内容
* @param charset 字符集
* @return 保存后的信息
*/
public static EmailUploadResult uploadBody(String email, String bodyContent, String charset) {
if (bodyContent == null) {
bodyContent = "";
}
InputStream inputStream = new ByteArrayInputStream(
bodyContent.getBytes(java.nio.charset.Charset.forName(charset))
);
return doUpload(email, "body.html", inputStream, BODY_PREFIX, "html");
}
/**
* 保存邮件附件
* @param email 邮件地址
* @param originalFileName 附件名称
* @param inputStream 文件流
* @return 保存后的信息
*/
public static EmailUploadResult uploadAttachment(String email, String originalFileName, InputStream inputStream) {
return doUpload(email, originalFileName, inputStream, ATTACHMENT_PREFIX, null);
}
private static EmailUploadResult doUpload(String email, String originalFileName, InputStream inputStream,
String typePrefix, String forcedExtension) {
try {
LocalDate today = LocalDate.now();
String datePath = today.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
String emailPath = normalizeEmail(email);
String extension = forcedExtension;
if (StringUtils.isEmpty(forcedExtension)) {
extension = getFileExtension(originalFileName);
if(StringUtils.isEmpty(extension)){
extension = "dat";
}
}
String fileName = System.currentTimeMillis() + "" + ThreadLocalRandom.current().nextInt(100,1000) + "." + extension;
String relativePath = joinFilePath(emailPath,typePrefix,datePath,fileName);
String fullPathStr = joinFilePath(LOCAL_BASE_PATH, relativePath);
Path fullPath = Paths.get(fullPathStr);
Path parentDir = fullPath.getParent();
if (parentDir != null && !Files.exists(parentDir)) {
Files.createDirectories(parentDir);
}
Files.copy(inputStream, fullPath, StandardCopyOption.REPLACE_EXISTING);
return new EmailUploadResult(
originalFileName,
extension,
Files.size(fullPath),
relativePath.replace("\\", "/")
);
} catch (IOException e) {
throw new RuntimeException("文件上传失败,路径:" + LOCAL_BASE_PATH + ",错误:" + e.getMessage(), e);
}
}
/**
* 构建本地
* @param relativePath 717406575_qq_com/Attachment/2025/06/03/1748927695653847.jpeg
* @return D:/data/email/717406575_qq_com/Attachment/2025/06/03/1748927695653847.jpeg
*/
public static String buildLocalFilePath(String relativePath) {
return joinFilePath(LOCAL_BASE_PATH, relativePath);
}
public static String buildPublicFileUrl(String relativePath) {
return joinUrl(LOCAL_SERVER_URL, LOCAL_STATIC_URL, relativePath).replace("\\", "/");
}
/**
* 拼接文件路径,自动忽略空路径,并进行路径标准化。
* 等价于 Paths.get(...).normalize().toString(),但支持多个参数并自动忽略空值。
* 示例:
* joinFilePath("D:/data", "email", "Body") => D:\data\email\Body
*
* @param paths 路径片段
* @return 合并后的标准路径字符串
*/
private static String joinFilePath(String... paths) {
if (paths == null || paths.length == 0) return "";
Path result = null;
for (String p : paths) {
if (p == null || p.trim().isEmpty()) continue; // 跳过空路径
if (result == null) {
result = Paths.get(p.trim()); // 初始化路径
} else {
result = result.resolve(p.trim()); // 追加路径
}
}
return (result == null) ? "" : result.normalize().toString(); // 返回标准化路径
}
/**
* 拼接 URL 路径,自动处理斜杠,确保不会出现重复或缺失的斜杠。
* 示例:
* joinUrl("http://localhost:8080/", "/static/email/", "/img.png") => http://localhost:8080/static/email/img.png
*
* @param parts URL 各部分片段
* @return 合并后的完整 URL 字符串
*/
private static String joinUrl(String... parts) {
if (parts == null || parts.length == 0) return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < parts.length; i++) {
String part = parts[i] == null ? "" : parts[i].trim();
if (part.isEmpty()) continue;
if (i == 0) {
// 第一个部分:去除尾部斜杠
sb.append(part.replaceAll("/+$", ""));
} else {
sb.append("/");
// 中间和结尾部分:去除前后多余的斜杠
sb.append(part.replaceAll("^/+", "").replaceAll("/+$", ""));
}
}
return sb.toString();
}
}
@@ -2,24 +2,35 @@ package com.email.core.fetch;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.email.constants.ResponseStatusConstants;
import com.email.core.EmailFileUtils;
import com.email.core.fetch.model.EmailFetchData;
import com.email.core.fetch.model.EmailFetchProgress;
import com.email.core.fetch.model.FolderWrapper;
import com.email.domain.entity.*;
import com.email.enums.ResponseEnum;
import com.email.enums.email.EmailAddressTypeEnum;
import com.email.enums.email.EmailBodyTypeEnum;
import com.email.enums.email.EmailFileTypeEnum;
import com.email.enums.email.EmailFolderTypeEnum;
import com.email.exception.auth.AuthException;
import com.email.utils.entity.EmailUploadResult;
import com.sun.javaws.progress.Progress;
import com.sun.mail.imap.IMAPFolder;
import com.sun.mail.imap.IMAPStore;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.IdGenerator;
import org.springframework.util.ObjectUtils;
import javax.mail.*;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MailDateFormat;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility;
import javax.validation.constraints.Email;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
@@ -70,7 +81,7 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
try {
threadStore = connect(userAccount);
processSingleFolder(threadStore, emailFolder, progressConsumer, folderConsumer, fetchDataConsumer);
processSingleFolder(emailFolder,userAccount, threadStore, progressConsumer, folderConsumer, fetchDataConsumer);
} catch (Exception e) {
log.error("处理文件夹 [{}] 异常:{}", emailFolder.getFullName(), e.getMessage(), e);
} finally {
@@ -89,11 +100,13 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
folderLatch.await();
}
private void processSingleFolder(Store store,
private void processSingleFolder(
EmailFolder emailFolder,
UserAccount userAccount,
Store store,
Consumer<EmailFetchProgress> progressConsumer,
Consumer<EmailFolder> folderConsumer,
Consumer<List<EmailFetchData>> fetchDataConsumer) {
Consumer<List<EmailFetchData>> emailConsumer) {
IMAPFolder imapFolder = null;
try {
imapFolder = (IMAPFolder) store.getFolder(emailFolder.getFullName());
@@ -105,10 +118,13 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
int totalMessages = imapFolder.getMessageCount();
if (totalMessages > 0) {
processEmailsInFolder(imapFolder,emailFolder);
try {
processEmailsInFolder(emailFolder,userAccount,imapFolder,emailConsumer);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
} catch (MessagingException e) {
log.error("打开或处理文件夹 [{}] 失败: {}", emailFolder.getFullName(), e.getMessage(), e);
} finally {
@@ -122,63 +138,163 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
}
}
private void processEmailsInFolder(IMAPFolder imapFolder,EmailFolder emailFolder) throws MessagingException {
private void processEmailsInFolder(EmailFolder emailFolder,
UserAccount userAccount,
IMAPFolder imapFolder,
Consumer<List<EmailFetchData>> emailConsumer) throws Exception {
if (!imapFolder.isOpen()) {
imapFolder.open(Folder.READ_ONLY);
}
Message[] messages = imapFolder.getMessages();
if (messages == null || messages.length == 0) {
log.info("文件夹 [{}] 没有邮件,跳过", imapFolder.getFullName());
return;
}
if (messages == null || messages.length == 0) return;
List<Message> messageList = Arrays.asList(messages);
int totalMessages = messageList.size();
log.info("文件夹 [{}] 邮件总数: {}", imapFolder.getFullName(), totalMessages);
// ✅ 可选:预拉字段避免 getSubject 慢
FetchProfile fetchProfile = new FetchProfile();
fetchProfile.add(FetchProfile.Item.ENVELOPE);
fetchProfile.add(FetchProfile.Item.FLAGS);
fetchProfile.add(FetchProfile.Item.CONTENT_INFO);
imapFolder.fetch(messages, fetchProfile);
int batchCount = (totalMessages + BATCH_SIZE - 1) / BATCH_SIZE;
CountDownLatch latch = new CountDownLatch(batchCount);
for (int i = 0; i < totalMessages; i += BATCH_SIZE) {
int end = Math.min(i + BATCH_SIZE, totalMessages);
List<Message> batchMessages = messageList.subList(i, end);
CountDownLatch latch = new CountDownLatch(messages.length);
for (Message message : messages) {
emailExecutor.submit(() -> {
try {
for (Message message : batchMessages) {
}
EmailFetchData data = processSingleEmail(imapFolder, message, emailFolder.getId(), userAccount.getEmail());
log.info("subject:{}",data.getEmailSummary().getSubject());
// 可选:收集并统一处理结果(如使用 synchronizedList)
// emailConsumer.accept(Collections.singletonList(data));
} catch (Exception e) {
log.error("批量任务异常", e);
log.error("处理邮件异常: {}", e.getMessage(), e);
} finally {
latch.countDown();
}
});
}
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
latch.await(); // 等待所有任务完成
try {
imapFolder.close(false);
} catch (MessagingException e) {
log.warn("关闭文件夹异常", e);
}
// if (!imapFolder.isOpen()) {
// imapFolder.open(Folder.READ_ONLY);
// }
//
// Message[] messages = imapFolder.getMessages();
// for (Message message : messages) {
// EmailFetchData data = processSingleEmail(imapFolder, message, emailFolder.getId(), userAccount.getEmail());
// System.out.println(data.getEmailSummary().getSubject());
// }
//
// long uidNext = imapFolder.getUIDNext(); // 下一封邮件 UID(不包含)
// long minUid = findMinUid(imapFolder); // 当前文件夹最小 UID
//
// if (minUid >= uidNext) {
// log.info("📭 文件夹 {} 无可拉取邮件", emailFolder.getFullName());
// return;
// }
//
// List<long[]> uidRanges = new ArrayList<>();
// for (long start = minUid; start < uidNext; start += BATCH_SIZE) {
// long end = Math.min(start + BATCH_SIZE - 1, uidNext - 1);
// uidRanges.add(new long[]{start, end});
// }
//
// CountDownLatch latch = new CountDownLatch(uidRanges.size());
//
// for (long[] range : uidRanges) {
// long startUid = range[0];
// long endUid = range[1];
//
// emailExecutor.submit(() -> {
// Store threadStore = null;
// IMAPFolder threadFolder = null;
// try {
// threadStore = connect(userAccount); // 每线程自己的连接
// threadFolder = (IMAPFolder) threadStore.getFolder(emailFolder.getFullName());
// threadFolder.open(Folder.READ_ONLY);
//
// Message[] messages = threadFolder.getMessagesByUID(startUid, endUid);
// if (messages == null || messages.length == 0) {
// return;
// }
//
// // ✅ 预拉字段,避免后续每个字段都触发 IO
// FetchProfile fp = new FetchProfile();
// fp.add(FetchProfile.Item.ENVELOPE);
// fp.add(FetchProfile.Item.FLAGS);
// fp.add(FetchProfile.Item.CONTENT_INFO);
// threadFolder.fetch(messages, fp);
//
// List<EmailFetchData> fetchDataList = new ArrayList<>(messages.length);
// for (Message message : messages) {
// try {
// EmailFetchData data = processSingleEmail(threadFolder, message, emailFolder.getId(), userAccount.getEmail());
// fetchDataList.add(data);
// } catch (Exception e) {
// log.error("处理邮件 UID={} 异常: {}", threadFolder.getUID(message), e.getMessage(), e);
// }
// }
//
// if (!fetchDataList.isEmpty()) {
// emailConsumer.accept(fetchDataList);
// }
// } catch (Exception e) {
// log.error("线程拉取 UID [{}-{}] 邮件异常: {}", startUid, endUid, e.getMessage(), e);
// } finally {
// try {
// if (threadFolder != null && threadFolder.isOpen()) threadFolder.close(false);
// if (threadStore != null && threadStore.isConnected()) threadStore.close();
// } catch (MessagingException e) {
// log.warn("关闭线程 IMAPFolder 或 Store 异常", e);
// }
// latch.countDown();
// }
// });
// }
//
// try {
// latch.await();
// } catch (InterruptedException e) {
// Thread.currentThread().interrupt();
// throw new RuntimeException("等待多线程拉取邮件中断", e);
// }
}
private long findMinUid(IMAPFolder folder) throws MessagingException {
Message[] msgs = folder.getMessages();
long min = Long.MAX_VALUE;
for (Message msg : msgs) {
long uid = folder.getUID(msg);
if (uid > 0 && uid < min) {
min = uid;
}
}
return min == Long.MAX_VALUE ? 1L : min;
}
private EmailFetchData processSingleEmail(Message message,EmailFolder emailFolder){
private EmailFetchData processSingleEmail(Folder folder,Message message,Long folderId,String userEmail) throws Exception {
EmailFetchData fetchData = new EmailFetchData();
EmailSummary emailSummary = processSingleEmailSummary(folder, message, folderId);
Long emailId = emailSummary.getId();
// emailAddress
List<EmailAddress> addressList = processSingleEmailAddress(message,emailId);
// emailAttachments
List<EmailFile> fileList = processSingleEmailAttachment(message, emailId, userEmail);
fetchData.setEmailSummary(emailSummary);
fetchData.setEmailAddressList(addressList);
fetchData.setEmailFileList(fileList);
return fetchData;
}
private EmailSummary processSingleEmailSummary(Folder folder,Message message,Long folderId) throws Exception {
EmailSummary emailSummary = new EmailSummary();
// emailId
long emailId = IdWorker.getId();
// subject
String subject = message.getSubject();
// messageId
@@ -191,8 +307,79 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
Date sentDate = resolveMailSentDate(message);
// isSeen
Integer isSeen = message.isSet(Flags.Flag.SEEN) ? 1 : 0;
// emailId
long emailId = IdWorker.getId();
// emailContent
EmailSummary emailContent = processSingleEmailContent(message);
emailSummary.setId(emailId)
.setSubject(subject)
.setMessageId(messageId)
.setImapUid(uid)
.setSentTime(sentDate)
.setIsSeen(isSeen)
.setBodyEncoding(emailContent.getBodyEncoding())
.setBodyCharset(emailContent.getBodyCharset())
.setBodyType(emailContent.getBodyType())
.setBodyPath(emailContent.getBodyPath())
.setBodySize(emailContent.getBodySize())
.setFolderId(folderId);
return emailSummary;
}
/**
* 处理单个邮件的各种地址
* @param message api
* @param emailId 该邮件的id
* @return 该封邮件返回的地址集合
* @throws MessagingException 处理异常
*/
private List<EmailAddress> processSingleEmailAddress(Message message, Long emailId) throws MessagingException {
List<EmailAddress> addressList = new ArrayList<>();
// 发件人
addAddresses(addressList, message.getFrom(), EmailAddressTypeEnum.FROM, emailId);
// 回复地址
addAddresses(addressList, message.getReplyTo(), EmailAddressTypeEnum.REPLY_TO, emailId);
// TO
addAddresses(addressList, message.getRecipients(Message.RecipientType.TO), EmailAddressTypeEnum.TO, emailId);
// CC
addAddresses(addressList, message.getRecipients(Message.RecipientType.CC), EmailAddressTypeEnum.CC, emailId);
// BCC
addAddresses(addressList, message.getRecipients(Message.RecipientType.BCC), EmailAddressTypeEnum.BCC, emailId);
return addressList;
}
private void addAddresses(List<EmailAddress> list, Address[] addresses, EmailAddressTypeEnum type, Long emailId) {
if (addresses == null) return;
for (Address address : addresses) {
if (address instanceof InternetAddress) {
InternetAddress ia = (InternetAddress) address;
String email = ia.getAddress();
String name = ia.getPersonal() != null ? ia.getPersonal() : email;
list.add(new EmailAddress()
.setAddress(email)
.setAddressName(name)
.setEmailId(emailId)
.setAddressType(type.getType()));
}
}
}
/**
* 处理邮件内容
* @param message api
* @throws Exception 处理邮件内容发生的异常
*/
private EmailSummary processSingleEmailContent(Message message) throws Exception {
EmailSummary emailSummary = new EmailSummary();
// 解析内容
Part part = message;
// bodyCharset
@@ -210,34 +397,20 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
: "";
// bodyContent
String bodyContent = resolveMailContent(part);
// 保存邮件到本地
EmailUploadResult emailUploadResult = EmailFileUtils.uploadBody(String.valueOf(emailSummary.getId()), bodyContent, bodyCharset);
emailSummary.setBodyCharset(bodyContent);
emailSummary.setBodyType(bodyType);
emailSummary.setBodyEncoding(bodyEncoding);
emailSummary.setBodyPath(emailUploadResult.getFilePath());
emailSummary.setBodySize(emailUploadResult.getFileSize());
return emailSummary;
}
private List<EmailAddress> processSingleEmailAddress(Message message) throws MessagingException {
List<EmailAddress> addressList = new ArrayList<>();
// 发件人
Address[] froms = message.getFrom();
// 回复地址
Address[] replyTos = message.getReplyTo();
// TO
Address[] tos = message.getRecipients(Message.RecipientType.TO);
// CC
Address[] ccs = message.getRecipients(Message.RecipientType.CC);
// BCC
Address[] bccs = message.getRecipients(Message.RecipientType.BCC);
return addressList;
}
private List<EmailFile> processSingleEmailFile(){
List<EmailFile> fileList = new ArrayList<>();
return fileList;
}
private String resolveMailContent(Part part) throws Exception {
if (part.isMimeType("text/html")) {
return (String) part.getContent();
@@ -271,6 +444,104 @@ public class ImapJavaMailEmailFetcher implements EmailFetcher {
return "";
}
/**
* 处理邮件附件/内嵌资源
* @param message api
* @param emailId 邮件ID
* @param userEmail 用户邮箱(保存附件时候需要)
* @return 邮件文件集合
* @throws MessagingException 异常
* @throws IOException 异常
*/
private List<EmailFile> processSingleEmailAttachment(Message message,Long emailId, String userEmail)
throws MessagingException, IOException {
List<EmailFile> attachments = new ArrayList<>();
MimeMessage mimeMessage = (MimeMessage) message;
Object content = mimeMessage.getContent();
if (content instanceof Multipart) {
processMultipart((Multipart) content,attachments,emailId,userEmail);
}
return attachments;
}
private void processMultipart(Multipart multipart, List<EmailFile> fileList, Long emailId, String userEmail)
throws MessagingException, IOException {
for (int i = 0; i < multipart.getCount(); i++) {
BodyPart part = multipart.getBodyPart(i);
Object partContent = part.getContent();
if (partContent instanceof Multipart) {
processMultipart((Multipart) partContent, fileList,emailId,userEmail);
continue;
}
String disposition = part.getDisposition();
String contentType = part.getContentType().toLowerCase();
String[] cidHeaders = part.getHeader("Content-ID");
boolean isInline = cidHeaders != null && cidHeaders.length > 0;
boolean isAttachment = Part.ATTACHMENT.equalsIgnoreCase(disposition)
|| (disposition == null && part.getFileName() != null);
// 只处理内嵌图片或合法附件
if (!(isAttachment || (isInline && contentType.startsWith("image")))) {
continue;
}
// 处理文件名并解码
String fileName = part.getFileName();
if (StringUtils.isBlank(fileName)) {
if (isInline && contentType.startsWith("image")) {
String cid = cidHeaders[0].replaceAll("[<>]", "");
fileName = "inline_" + cid + ".png";
} else {
continue;
}
}
try {
fileName = decodeMimeName(fileName); // 解码文件名
} catch (UnsupportedEncodingException e) {
// 如果解码失败,继续使用原始文件名
e.printStackTrace();
}
// 替换非法字符(如 < > 等)为下划线或其他合法字符
fileName = fileName.replaceAll("[<>:\"/\\|?*]", "_");
EmailFile emailFile = new EmailFile();
emailFile.setEmailId(emailId);
// 选择上传路径和文件
InputStream inputStream = part.getInputStream();
EmailUploadResult emailUploadResult = com.email.utils.EmailFileUtils.uploadAttachment(userEmail, fileName, inputStream);
emailFile.setFileName(emailUploadResult.getFileName());
emailFile.setFileExtension(emailUploadResult.getFileExtension());
emailFile.setFileSize(emailUploadResult.getFileSize());
emailFile.setFilePath(emailUploadResult.getFilePath());
// 设置 CID(如果是内嵌图片)
if (isInline) {
String cid = cidHeaders[0].replaceAll("[<>]", "");
emailFile.setEmailCid(cid);
emailFile.setFileType(EmailFileTypeEnum.INLINE.getCode());
}else{
emailFile.setEmailCid("");
emailFile.setFileType(EmailFileTypeEnum.ATTACHMENT.getCode());
}
fileList.add(emailFile);
}
}
private String decodeMimeName(String encodedName) throws UnsupportedEncodingException {
return MimeUtility.decodeText(encodedName);
}
/**
* 解析邮件的 发送/接受 时间
* @param message 消息