java - 从临时目录读取 SQLite 文件时获取 java.io.EOFException

标签 java compression lzma apache-commons-compress

我在从临时目录读取 SQLite 文件时看到了 EOFException 异常。以下是读取文件的代码。而且并不总是看到异常。考虑在 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))) { 行抛出。以下是异常(exception)情况。

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

最佳答案

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

由于 LZMA 流也用于 7z 和 xz 等容器格式,LZMAOutputStreamLZMAInputStream 类还提供了用于读取/写入没有 header 的流的构造器。

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

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

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

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

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

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

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

关于java - 从临时目录读取 SQLite 文件时获取 java.io.EOFException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62041957/

相关文章:

algorithm - 减少图表的数据点,同时保持其主要特征

installation - 安装程序与 zip 或可执行 exe?

http - Nginx 服务器内容 gzip 压缩不起作用

c# - 在 .NET 服务器上压缩/解压缩在客户端上使用 lz-string.js 编码的字符串

Python解压相对性能?

JavaFx 2 使用单列创建 TableView

java - 获取应用于 WebElement 的 css 类

java - 在Java中运行代码x秒?

python - 使用 numpy fromfile 结合 lzma open 读取二进制文件

java - 检查 Android 应用是否已安装,第二次检查时返回错误