3

从临时目录读取文件时,我看到EOFException异常。SQLite以下是读取文件的代码。而且并不总是看到例外。考虑到 50K 个文件,它会出现 3 到 4 次。

public static byte[] decompressLzmaStream(InputStream inputStream, int size) 
    throws CompressorException, IOException {

    if(size < 1) {
        size = 1024 * 100;
    }

    try(LZMACompressorInputStream lzmaInputStream = 
                                           new LZMACompressorInputStream(inputStream);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(size)) {
        byte[] buffer = new byte[size];

        int length;
        while (-1 != (length = lzmaInputStream.read(buffer))) {
            byteArrayOutputStream.write(buffer, 0, length);
        }
        byteArrayOutputStream.flush();
        return byteArrayOutputStream.toByteArray();
    }
}

我正在使用以下依赖项进行解压

 <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.20</version>
</dependency>

异常 while (-1 != (length = lzmaInputStream.read(buffer))) {在行抛出。以下是例外。

java.io.EOFException: null at java.io.DataInputStream.readUnsignedByte(DataInputStream.java:290) 
at org.tukaani.xz.rangecoder.RangeDecoderFromStream.normalize(Unknown Source) 
at org.tukaani.xz.rangecoder.RangeDecoder.decodeBit(Unknown Source) 
at org.tukaani.xz.lzma.LZMADecoder.decode(Unknown Source) 
at org.tukaani.xz.LZMAInputStream.read(Unknown Source) 
at org.apache.commons.compress.compressors.lzma.
    LZMACompressorInputStream.read(LZMACompressorInputStream.java:62) 
at java.io.InputStream.read(InputStream.java:101)  

任何人都对以下构造函数有任何想法commons-compress

// I am using this constructor of LZMACompressorInputStream

public LZMACompressorInputStream(InputStream inputStream) throws IOException {
    this.in = new LZMAInputStream(this.countingStream = new CountingInputStream(inputStream), -1);
} 

// This is added in later version of commons-compress, what is memoryLimitInKb
public LZMACompressorInputStream(InputStream inputStream, int memoryLimitInKb) throws IOException {
    try {
        this.in = new LZMAInputStream(this.countingStream = new CountingInputStream(inputStream), memoryLimitInKb);
    } catch (MemoryLimitException var4) {
        throw new org.apache.commons.compress.MemoryLimitException((long)var4.getMemoryNeeded(), var4.getMemoryLimit(), var4);
    }
}

当我阅读LZMA 流时,我们需要将未压缩的大小传递给此处的构造函数 --> https://issues.apache.org/jira/browse/COMPRESS-286?page=com.atlassian.jira.plugin.system。 issuetabpanels%3Acomment-tabpanel&focusedCommentId=14109417#comment-14109417

4

1 回答 1

3

LZMA 解码器需要知道压缩流何时结束。如果在压缩过程中知道未压缩的大小,则流的标头(位于流的开头)将包含未压缩的大小。当解码器的输出达到这个大小时,解码器就知道到达了流的末尾。如果在压缩过程中不知道未压缩的大小,则标头将不包含该大小。在这种情况下,编码器假定流以流结束标记明确终止。

由于 LZMA 流也用于 7z 和 xz 等容器格式,因此LZMAOutputStreamLZMAInputStream类还提供了用于读取/写入流的构造函数,而无需标头。

COMPRESS-286是关于解压缩包含 LZMA 压缩条目的 7z 存档。7z 存档包含没有标头的 LZMA 流。通常存储在 LZMA 标头中的信息与流分开存储。SevenZFile用于读取 7z 档案的Apache commons类LZMAInputStream使用以下构造函数创建对象:

LZMAInputStream(InputStream in, long uncompSize, byte propsByte, int dictSize)

构造函数的附加参数表示通常存储在 LZMA 流开头的标头中的信息。COMPRESS-286 的修复确保了未压缩的大小(之前丢失)也被移交给 LZMAInputStream。

LZMACompressorInputStream也使用,LZMAInputStream但它假定压缩流包含显式标头。因此,不可能通过它的构造函数来传递信息。

memoryLimitInKb参数仅限制用于解压的内存,与未压缩大小无关。所需内存的主要贡献者是字典的选定大小。此大小在压缩期间指定,也存储在流的标头中。其最大值为 4 GB。通常字典的大小小于未压缩的大小。大于未压缩大小的字典绝对浪费内存。损坏的 LZMA 标头很容易导致 OOM 错误,并且被操纵的流甚至为拒绝服务攻击打开了大门。因此,当您读取未经验证的 LZMA 流时,限制最大内存使用量是明智的。

总结一下:由于您没有阅读带有 LZMA 压缩条目的 7z 存档,因此COMPRESS-286与您的问题无关。但是类似的堆栈跟踪可能表明您的流标头有问题。

确保使用以下实例压缩数据LZACompressorOutputStream(自动选择字典大小、所有其他参数并确保写入标头)。如果您应该 LZAOutputStream直接使用,请确保使用实际写入标头的实例。

于 2020-05-30T13:40:49.913 回答