使用 apache common compress/org.tukaani.xz 在 java 中解码 LZMA 压缩 zip 文件时出现问题

Raj*_*Raj 5 java compression apache lzma

尝试解码 LZMA 压缩 xls 文件时出现org.tukaani.xz.UnsupportedOptionsException:未压缩大小太大错误。而非 LZMA 文件可以毫无问题地解压/解码。这两种情况都压缩相同的 xls 文件。

我正在使用 Apache commons compress 和 org.tukaani.xz。

示例代码供参考

package com.concept.utilities.zip;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream;

public class ApacheComm {

    public void extractLZMAZip(File zipFile, String compressFileName, String destFolder) {

        ZipFile zip = null;
        try {

            zip = new ZipFile(zipFile);
            ZipArchiveEntry zipArchiveEntry = zip.getEntry(compressFileName);
            if (null != zipArchiveEntry) {
                String name = zipArchiveEntry.getName();

                // InputStream is = zip.getInputStream(zipArchiveEntry);
                InputStream israw = zip.getRawInputStream(zipArchiveEntry);

                LZMACompressorInputStream lzma = new LZMACompressorInputStream(israw);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != zip)
                ZipFile.closeQuietly(zip);
        }
    }

    public static void main(String[] args) throws IOException {

        ApacheComm c = new ApacheComm();
        try {
            c.extractLZMAZip(new File("H:\\archives\\rollLZMA.zip"), "roll.xls", "H:\\archives\\");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

错误

org.tukaani.xz.UnsupportedOptionsException: Uncompressed size is too big
    at org.tukaani.xz.LZMAInputStream.initialize(Unknown Source)
    at org.tukaani.xz.LZMAInputStream.<init>(Unknown Source)
    at org.apache.commons.compress.compressors.lzma.LZMACompressorInputStream.<init>(LZMACompressorInputStream.java:50)
    at com.concept.utilities.zip.ApacheComm.extractLZMAZip(ApacheComm.java:209)
    at com.concept.utilities.zip.ApacheComm.main(ApacheComm.java:224)
Run Code Online (Sandbox Code Playgroud)

我错过了什么吗?有没有其他方法可以使用压缩方法 = LZMA 解码 zip 文件

小智 4

您的代码不起作用的原因是 Zip LZMA 压缩数据段与普通压缩 LZMA 文件相比具有不同的标头。

您可以在https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT阅读规范(4.4.4 通用位标志,5.8 LZMA - 方法 14),但引用重要部分:

5.8.5 [...] LZMA 压缩数据段将由 LZMA 属性标头和后跟 LZMA 压缩数据组成,如下所示:

[LZMA properties header for file 1]
[LZMA compressed data for file 1]
Run Code Online (Sandbox Code Playgroud)

[...]

5.8.8 LZMA Properties Header 中属性信息的存储字段如下:

LZMA Version Information 2 bytes
LZMA Properties Size 2 bytes
LZMA Properties Data variable, defined by "LZMA Properties Size"
Run Code Online (Sandbox Code Playgroud)

5.8.8.1 LZMA 版本信息 - 该字段标识用于压缩文件的 LZMA SDK 版本。第一个字节将存储 LZMA SDK 的主版本号,第二个字节将存储次版本号。

5.8.8.2 LZMA 属性大小 - 该字段定义剩余属性数据的大小。通常这个大小应该由 SDK 的版本决定。包含此大小字段是为了方便,并有助于避免将来由于此压缩算法的更改而出现任何歧义。

5.8.8.3 LZMA 属性数据 - 这个可变大小的字段记录 LZMA SDK 定义的解压缩器所需的值。存储在该字段中的数据应该使用“LZMA 版本信息”字段定义的 SDK 版本中的 WriteCoderProperties() 来获取。

代码示例:

[LZMA properties header for file 1]
[LZMA compressed data for file 1]
Run Code Online (Sandbox Code Playgroud)