diff --git a/code/email/Email-Springboot/src/main/java/com/email/Test.java b/code/email/Email-Springboot/src/main/java/com/email/Test.java index 44c6756d..43e90073 100644 --- a/code/email/Email-Springboot/src/main/java/com/email/Test.java +++ b/code/email/Email-Springboot/src/main/java/com/email/Test.java @@ -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()); + } } ); } diff --git a/code/email/Email-Springboot/src/main/java/com/email/core/EmailFileUtils.java b/code/email/Email-Springboot/src/main/java/com/email/core/EmailFileUtils.java new file mode 100644 index 00000000..377e6abc --- /dev/null +++ b/code/email/Email-Springboot/src/main/java/com/email/core/EmailFileUtils.java @@ -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(); + } +} diff --git a/code/email/Email-Springboot/src/main/java/com/email/core/fetch/ImapJavaMailEmailFetcher.java b/code/email/Email-Springboot/src/main/java/com/email/core/fetch/ImapJavaMailEmailFetcher.java index 1f88b0cb..bc47f2a8 100644 --- a/code/email/Email-Springboot/src/main/java/com/email/core/fetch/ImapJavaMailEmailFetcher.java +++ b/code/email/Email-Springboot/src/main/java/com/email/core/fetch/ImapJavaMailEmailFetcher.java @@ -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 progressConsumer, Consumer folderConsumer, - Consumer> fetchDataConsumer) { + Consumer> 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> 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 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 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 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 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 addressList = processSingleEmailAddress(message,emailId); + + // emailAttachments + List 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 processSingleEmailAddress(Message message, Long emailId) throws MessagingException { + List 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 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 processSingleEmailAddress(Message message) throws MessagingException { - List 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 processSingleEmailFile(){ - List 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 processSingleEmailAttachment(Message message,Long emailId, String userEmail) + throws MessagingException, IOException { + List 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 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 消息