无效的标头读取 xls 文件

8

我正在读取本地系统上的一个Excel文件。我正在使用POI jar版本3.7,但是出现了“无效的标头签名”错误; 读取-2300849302551019537或十六进制0xE011BDBFEFBDBFEF, 期望为-2226271756974174256或十六进制0xE11AB1A1E011CFD0。

使用Excel打开xls文件正常。

发生错误的代码块: 有任何想法吗?

/**
 * create a new HeaderBlockReader from an InputStream
 *
 * @param stream the source InputStream
 *
 * @exception IOException on errors or bad data
 */
public HeaderBlockReader(InputStream stream) throws IOException {
    // At this point, we don't know how big our
    //  block sizes are
    // So, read the first 32 bytes to check, then
    //  read the rest of the block
    byte[] blockStart = new byte[32];
    int bsCount = IOUtils.readFully(stream, blockStart);
    if(bsCount != 32) {
        throw alertShortRead(bsCount, 32);
    }

    // verify signature
    long signature = LittleEndian.getLong(blockStart, _signature_offset);

    if (signature != _signature) {
        // Is it one of the usual suspects?
        byte[] OOXML_FILE_HEADER = POIFSConstants.OOXML_FILE_HEADER;
        if(blockStart[0] == OOXML_FILE_HEADER[0] &&
            blockStart[1] == OOXML_FILE_HEADER[1] &&
            blockStart[2] == OOXML_FILE_HEADER[2] &&
            blockStart[3] == OOXML_FILE_HEADER[3]) {
            throw new OfficeXmlFileException("The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)");
        }
        if ((signature & 0xFF8FFFFFFFFFFFFFL) == 0x0010000200040009L) {
            // BIFF2 raw stream starts with BOF (sid=0x0009, size=0x0004, data=0x00t0)
            throw new IllegalArgumentException("The supplied data appears to be in BIFF2 format.  "
                    + "POI only supports BIFF8 format");
        }

        // Give a generic error
        throw new IOException("Invalid header signature; read "
                              + longToHex(signature) + ", expected "
                              + longToHex(_signature));
    }

1
你从代码中抛出了这个异常?throw new IOException?我对你的问题感到困惑,因为你自己抛出了异常,而现在又问为什么要这样做? - Sajan Chandran
异常由Apache Poi库抛出,上面是抛出它的代码。 - dutchman79
Apache POI没有抛出错误,你的代码抛出了“new IOException("Invalid header signature; read"。 - Sajan Chandran
HeaderBlockReader(InputStream stream) 构造函数是 Apache POI 库的一部分。我的代码将现有的 xls 文件作为流获取,然后我想使用此 InputStream 创建一个新的 POIFileSystem。 - dutchman79
3个回答

17

只是一种想法,如果您使用maven,请确保在资源标记中将过滤设置为false。否则,maven在复制阶段会倾向于损坏xls文件。


在你的pom.xml文件中的<resources>标签内。 - Felix

13

这个异常告诉你,你的文件不是有效的基于OLE2的 .xls 文件。

能够在Excel中打开文件并不能说明什么 - Excel 会乐意地打开它知道的任何文件,无论它的扩展名是什么。如果你将一个 .csv 文件重命名为 .xls,Excel 仍然会打开它,但是重命名并没有神奇地使它成为 .xls 格式,所以 POI 不会为你打开它。

如果你在 Excel 中打开文件并执行“另存为”操作,它将允许你将其写成真正的 Excel 文件。如果你想知道它到底是什么文件,请尝试使用Apache Tika - 带有--detect的Tika CLI应该能告诉你。

如何确定它不是有效的文件?如果你查看 Microsoft 的OLE2 文件格式规范文档,并转到第 2.2 节,你将看到以下内容:

Header Signature (8 bytes): Identification signature for the compound file structure, and MUST be set to the value 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1.

将这些字节翻转过来(OLE2 是小端),你就可以得到 0xE11AB1A1E011CFD0,也是异常中的魔数。你的文件不以该魔数开头,因此并不是有效的 OLE2 文档,因此 POI 给出了该异常。


好的,用Excel打开并另存为会导致相同的问题。然后我下载了Apache Tika jar,并使用--detect选项运行它,结果显示:application/vnd.ms-excel。 - dutchman79
你的文件系统可能出了问题吗?磁盘快要挂了吗?请查看更新后的答案,你所拥有的文件头并不是有效的! - Gagravarr
更加奇怪的是:当我直接在src/main/resources文件夹中通过地址读取相同的模板文件时,它可以工作。但是当我尝试使用getResourceAsStream()读取它时,我会得到这个无效的头值... - dutchman79
你的程序是否在流上进行了一些奇怪的utf8/ascii或native/unicode转换?我建议你创建一个小的(约50字节)测试文件,尝试加载它并比较字节,然后基于此提出一个新问题! - Gagravarr
这是一份非常好的解释,@Gagravarr。你知道吗,为什么Excel可以理解不合法的OLE2文件并打开它,而我们无法使用Apache POI以编程方式解析该文件吗? - Bhuvanesh Mani
Excel支持广泛的文件格式,其中并非所有都是OLE2基础。尝试使用Apache Tika查找您的文件真正的格式,然后使用相应的库! - Gagravarr

0
如果您的项目是Maven项目,以下代码可能会有所帮助:
/**
 * Get input stream of excel.
 * <p>
 *     Get excel from src dir instead of target dir to avoid causing POI header exception.
 * </p>
 * @param fileName file in dir PROJECT_PATH/src/test/resources/excel/ , proceeding '/' is not needed.
 * @return
 */
private static InputStream getExcelInputStream(String fileName){
    InputStream inputStream = null;
    try{
        inputStream = new FileInputStream(getProjectPath() + "/src/test/resources/excel/" + fileName);
    }catch (URISyntaxException uriE){
        uriE.printStackTrace();
    }catch (FileNotFoundException fileE){
        fileE.printStackTrace();
    }
    return inputStream;
}

private static String getProjectPath() throws URISyntaxException{
    URL url = YourServiceImplTest.class.getResource("/");
    Path path = Paths.get(url.toURI());
    Path subPath = path.subpath(0, path.getNameCount() -2);
    return "/" + subPath.toString();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接