接收邮件时如何从Multipart体中提取Outlook内嵌图片?
解决Outlook内嵌邮件图片的捕获与持久化问题
我懂你现在的困扰——接收Outlook发来的邮件时,里面的内嵌图片根本看不到,还得把这些图片存下来配合CKEditor使用。看了你的代码,发现核心问题是只处理了文本类型的BodyPart,完全没碰内嵌的图片资源。Outlook的内嵌图片一般是以inline的disposition存在,还带Content-ID(也就是cid),HTML正文里会用cid:xxx来引用这些图片。
下面是修改后的代码,我一步步给你讲怎么捕获并持久化这些图片,同时把HTML里的引用改成CKEditor能识别的路径:
第一步:写个工具方法存图片
先整个辅助方法,用来把图片存到本地或者你的存储服务,这里先以本地文件为例:
private String saveInlineImage(BodyPart bodyPart, String storageDir) throws IOException, MessagingException { // 先确保存储目录存在,不存在就创建 File dir = new File(storageDir); if (!dir.exists()) { dir.mkdirs(); } // 优先用Content-ID当文件名,没有的话用附件自带的文件名,再不行就默认加个后缀 String cid = bodyPart.getHeader("Content-ID")[0].replaceAll("[<>]", ""); String fileName = bodyPart.getFileName(); if (fileName == null || fileName.isEmpty()) { fileName = cid + ".png"; // 这里默认用png,你也可以根据ContentType动态调整 } // 把图片写到本地 String filePath = storageDir + File.separator + fileName; try (InputStream in = bodyPart.getInputStream(); OutputStream out = new FileOutputStream(filePath)) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } } // 返回CKEditor能访问的路径,比如你的静态资源目录路径或者云存储URL return "/static/mail-images/" + fileName; }
第二步:修改邮件解析逻辑
现在更新你原来的代码,加上内嵌图片的处理逻辑:
try (InputStream stream = new ByteArrayInputStream(Base64.getMimeDecoder().decode(mail))) { MimeMessage message = new MimeMessage(null, stream); Object messageContent = message.getContent(); String body = ""; // 存一下cid和实际图片路径的映射,后面替换HTML里的引用用 Map<String, String> cidToPathMap = new HashMap<>(); if (messageContent instanceof String) { body = (String) messageContent; } else if (messageContent instanceof MimeMultipart) { MimeMultipart content = (MimeMultipart) messageContent; for (int i = 0; i < content.getCount(); i++) { BodyPart bodyPart = content.getBodyPart(i); String disposition = bodyPart.getDisposition(); String contentType = bodyPart.getContentType(); // 先处理内嵌图片:inline类型,且是图片格式的BodyPart if ((disposition == null || disposition.equalsIgnoreCase(Part.INLINE)) && contentType.startsWith("image/")) { // 保存图片,拿到可访问路径 String imagePath = saveInlineImage(bodyPart, "/your/local/storage/path"); // 把Content-ID的<>去掉,方便后面替换 String cid = bodyPart.getHeader("Content-ID")[0].replaceAll("[<>]", ""); cidToPathMap.put(cid, imagePath); continue; // 图片部分不用当正文处理,直接跳过 } // 处理文本正文,保留你原来的逻辑,最后要替换cid引用 if (disposition == null || disposition.equalsIgnoreCase(Part.INLINE)) { Object object = bodyPart.getContent(); if (object instanceof String) { body = object.toString(); } else if (object instanceof MimeMultipart) { MimeMultipart mimeMultipart = (MimeMultipart) object; String plainBody = null; String htmlBody = null; for (int j = 0; j < mimeMultipart.getCount(); j++) { BodyPart multipartBodyPart = mimeMultipart.getBodyPart(j); String multipartContentType = multipartBodyPart.getContentType(); if (multipartContentType.contains(MediaType.TEXT_HTML)) { htmlBody = multipartBodyPart.getContent().toString(); } else if (multipartContentType.contains(MediaType.TEXT_PLAIN)) { plainBody = multipartBodyPart.getContent().toString(); } } body = htmlBody != null ? htmlBody : plainBody; } } } // 最后把HTML里的cid:xxx替换成实际的图片路径,这样CKEditor就能显示了 if (!cidToPathMap.isEmpty() && body.contains("cid:")) { for (Map.Entry<String, String> entry : cidToPathMap.entrySet()) { body = body.replace("cid:" + entry.getKey(), entry.getValue()); } } } // 现在body就是处理好的带正确图片路径的HTML,直接给CKEditor用就行 // System.out.println("处理后的正文:" + body); } catch (Exception e) { e.printStackTrace(); }
几个关键细节要注意
- Content-ID的处理:Outlook的HTML里用
cid:xxx引用图片,我们得把这个cid对应到实际存储的路径,不然CKEditor找不到图片。 - 图片存储适配:示例里是存本地,你可以改成上传到OSS、云存储,只要返回的是CKEditor能访问的公开URL就行。
- 类型判断:用
contentType.startsWith("image/")来识别图片,避免误处理其他inline类型的资源(比如某些内嵌的音频)。 - 嵌套多部分处理:如果邮件正文是嵌套的MimeMultipart,原逻辑已经覆盖了,现在新增的图片处理会优先捕获内嵌图片。
这样改完之后,你就能成功抓到Outlook邮件里的内嵌图片,而且CKEditor也能正常显示这些图片了。
内容的提问来源于stack exchange,提问作者Vijay
相关产品推荐
相关产品推荐

