Java中读取Outlook .MSG文件原始内容的方案咨询
嘿,我刚好折腾过类似的需求,给你几个实用的方案来解决读取Outlook .MSG完整原始内容的问题:
方案1:获取整个.MSG文件的原始二进制内容(如果需要完整文件数据)
.MSG本身就是二进制格式,直接转String肯定会乱码或者丢失数据——毕竟它不是纯文本文件。你可以直接读取整个文件的字节数组来保存原始内容,要是需要可视化查看,转成十六进制字符串就好:
import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class MsgRawContentReader { public static void main(String[] args) throws IOException { File msgFile = new File("你的文件路径/xxx.msg"); byte[] rawBinaryContent = new byte[(int) msgFile.length()]; // 用try-with-resources确保流自动关闭 try (FileInputStream fis = new FileInputStream(msgFile)) { fis.read(rawBinaryContent); } // 如果要查看原始内容的十六进制形式 String hexView = bytesToHex(rawBinaryContent); System.out.println(hexView); // 也可以把字节数组写入新文件,完全还原原始.MSG // Files.write(Paths.get("复制的原始文件.msg"), rawBinaryContent); } // 辅助方法:把字节数组转成十六进制字符串 private static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02X ", b)); } return sb.toString().trim(); } }
方案2:用Apache POI HSMF挖掘所有MAPI属性(如果需要邮件的所有原始字段)
如果你要的不是整个文件的二进制,而是邮件内部的所有原始字段(比如POI默认API没暴露的那些),可以用HSMF的底层API来获取所有MAPI属性,包括自定义字段:
import org.apache.poi.hsmf.MAPIMessage; import org.apache.poi.hsmf.datatypes.MAPIProperty; import org.apache.poi.hsmf.datatypes.PropertyValue; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import java.io.FileInputStream; import java.io.IOException; import java.util.Map; public class FullMsgPropertiesReader { public static void main(String[] args) throws IOException { try (FileInputStream fis = new FileInputStream("你的文件路径/xxx.msg"); POIFSFileSystem poifs = new POIFSFileSystem(fis)) { MAPIMessage msg = new MAPIMessage(poifs); // 获取所有标准MAPI属性,包括POI高层API没单独封装的 Map<MAPIProperty, PropertyValue> allStandardProps = msg.getAllProperties(); System.out.println("=== 所有标准MAPI属性 ==="); for (Map.Entry<MAPIProperty, PropertyValue> entry : allStandardProps.entrySet()) { MAPIProperty prop = entry.getKey(); PropertyValue value = entry.getValue(); System.out.printf("属性名: %s | ID: 0x%04X | 类型: %s | 值: %s%n", prop.getName(), prop.getId(), prop.getType(), value.getValue()); } // 获取用户自定义的属性 Map<MAPIProperty, PropertyValue> customProps = msg.getCustomProperties(); if (!customProps.isEmpty()) { System.out.println("\n=== 自定义属性 ==="); for (Map.Entry<MAPIProperty, PropertyValue> entry : customProps.entrySet()) { MAPIProperty prop = entry.getKey(); PropertyValue value = entry.getValue(); System.out.printf("自定义属性ID: 0x%04X | 类型: %s | 值: %s%n", prop.getId(), prop.getType(), value.getValue()); } } } } }
方案3:直接操作OLE2复合文档读取所有流(极端情况兜底)
.MSG本质是OLE2复合文档,里面包含多个嵌套的流和目录。如果POI的HSMF还是覆盖不到你需要的内容,可以直接遍历所有OLE流,拿到最底层的原始数据:
import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class AllOleStreamsReader { public static void main(String[] args) throws IOException { try (FileInputStream fis = new FileInputStream("你的文件路径/xxx.msg"); POIFSFileSystem poifs = new POIFSFileSystem(fis)) { DirectoryEntry rootDir = poifs.getRoot(); // 递归遍历所有OLE目录和流 traverseOleStructure(rootDir, ""); } } private static void traverseOleStructure(DirectoryEntry dir, String currentPath) throws IOException { for (org.apache.poi.poifs.filesystem.Entry entry : dir) { String fullPath = currentPath + "/" + entry.getName(); if (entry.isDirectoryEntry()) { // 如果是目录,递归进去 traverseOleStructure((DirectoryEntry) entry, fullPath); } else { // 如果是文档流,读取原始内容 DocumentEntry docEntry = (DocumentEntry) entry; System.out.printf("读取OLE流: %s | 大小: %d 字节%n", fullPath, docEntry.getSize()); try (InputStream stream = docEntry.getInputStream()) { byte[] streamRawContent = stream.readAllBytes(); // 这里可以对streamRawContent做任何你需要的处理,比如分析二进制结构 } } } } }
额外注意事项
- 绝对不要把.MSG的二进制内容直接转成
String——二进制里包含大量非文本字节,转字符串会损坏数据,一定要用字节数组或者十六进制字符串处理。 - 如果需要深入解析.MSG的结构,可以参考微软官方的MS-OXMSG文档,里面详细定义了.MSG的OLE结构和MAPI属性规范。
内容的提问来源于stack exchange,提问作者tiamat
相关产品推荐
相关产品推荐

