使用Apache POI提取Excel文本类附件存在额外数据无法正常打开如何解决
问题根因
你直接调用ObjectData.getObjectData()获取到的是完整的OLE对象数据,包含了OLE结构化存储的封装头信息,不是原始的附件文件内容。exe、png这类格式对文件开头的冗余数据容错性较强,所以可以正常打开,而txt、pdf等格式要求文件内容严格匹配格式规范,多余的头部数据就会导致文件损坏无法打开。
正确实现代码
需要借助POI的OLE解析能力提取原始文件内容,修改后的代码如下:
import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.commons.io.IOUtils; import java.io.ByteArrayInputStream; import java.io.InputStream; // 原有逻辑保留到判断ObjectData的分支 if (next instanceof ObjectData) { ObjectData o = (ObjectData) next; byte[] objectData = o.getObjectData(); byte[] rawFileContent = null; // 解析OLE结构化存储提取原始文件 try (ByteArrayInputStream bis = new ByteArrayInputStream(objectData); POIFSFileSystem poifs = new POIFSFileSystem(bis)) { // OLE对象中存储原始文件内容的节点通常为"CONTENTS" DirectoryEntry root = poifs.getRoot(); if (root.hasEntry("CONTENTS")) { DocumentEntry entry = (DocumentEntry) root.getEntry("CONTENTS"); try (InputStream entryIs = poifs.createDocumentInputStream(entry)) { rawFileContent = IOUtils.toByteArray(entryIs); } } else { // 特殊OLE对象无CONTENTS节点时直接用原始数据兜底 rawFileContent = objectData; } } catch (Exception e) { // 非OLE格式的嵌入对象直接使用原始数据 rawFileContent = objectData; } IOUtil.write(rawFileContent, outputPath); } else if (next instanceof Picture) { // 图片提取逻辑无需修改,Picture.getData()返回的就是原始图片二进制内容 Picture o = (Picture) next; IOUtil.write(o.getData(), outputPath); }
注意事项
- 需要额外引入
poi-scratchpad依赖处理OLE结构,版本需和你当前使用的POI核心版本保持一致 - 部分老旧Excel文件的OLE节点名可能存在差异,若CONTENTS节点不存在可自行遍历root下的子节点匹配对应文件内容
- 可通过
ObjectData.getFileName()获取原始文件名后缀,避免输出文件格式不匹配
内容的提问来源于stack exchange,提问作者516628
相关产品推荐
相关产品推荐

