Получение java .io.EOFException при чтении файла SQLite из временного каталога - PullRequest
3 голосов
/ 27 мая 2020

Я вижу исключение 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

1 Ответ

3 голосов
/ 30 мая 2020

Декодер LZMA должен знать, когда заканчивается сжатый поток. Если несжатый размер был известен во время сжатия, заголовок потока (расположенный в начале потока) будет содержать несжатый размер. Когда выходной сигнал декодера достигает этого размера, декодер знает, что достигнут конец потока. Если несжатый размер не был известен во время сжатия, заголовок не будет содержать размер. В этом случае кодировщик предполагает, что поток явно завершен маркером конца потока.

Поскольку потоки LZMA также используются в форматах контейнеров, таких как 7z и xz, классы LZMAOutputStream и LZMAInputStream также предоставляют конструкторы для чтения / записи потоков без заголовка.

COMPRESS-286 - это распаковка архива 7z, который содержит запись со сжатием LZMA. Архив 7z содержит потоки LZMA без заголовка. Информация, которая обычно хранится в заголовке LZMA, хранится отдельно от потока. Apache commons SevenZFile класс для чтения архивов 7z создает LZMAInputStream объектов со следующим конструктором:

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

Дополнительные параметры конструктора представляют информацию, которая обычно хранится в заголовке в начале потока LZMA. Исправление COMPRESS-286 гарантировало, что также несжатый размер (отсутствовал ранее) был передан в LZMAInputStream.

LZMACompressorInputStream также использует LZMAInputStream, но предполагает, что сжатый поток содержит явное заголовок. Следовательно, невозможно передать информацию через его конструктор.

Параметр memoryLimitInKb ограничивает только память, которая используется для распаковки, и не имеет ничего общего с несжатым размером. Основной вклад в требуемую память - выбранный размер словаря. Этот размер указывается во время сжатия и также сохраняется в заголовке потока. Его максимальное значение - 4 ГБ. Обычно размер словаря меньше размера несжатого. Словарь, размер которого превышает несжатый, является пустой тратой памяти. Поврежденный заголовок LZMA может легко привести к ошибке OOM, а управляемый поток даже открывает двери для атак типа «отказ в обслуживании». Поэтому разумно ограничить максимальное использование памяти при чтении непроверенного потока LZMA.

Подводя итог : поскольку вы не читаете архив 7z со сжатой записью LZMA, COMPRESS-286 не имеет ничего общего с вашей проблемой. Но подобная трассировка стека может быть индикатором того, что что-то не так с заголовками вашего потока.

Убедитесь, что данные сжаты с экземпляром LZACompressorOutputStream (автоматически выбирает размер словаря, все остальные параметры и гарантирует, что заголовок написан). Если вы должны использовать LZAOutputStream напрямую, убедитесь, что вы используете экземпляр, который действительно записывает заголовок.

...