Kotlin закрывает ZipInputStream до прочтения всех записей

Я пытаюсь прочитать сжатый файл, используя Kotlin и ZipInputStream в ByteArrayOutputStream()

val f = File("/path/to/zip/myFile.zip")
val zis = ZipInputStream(FileInputStream(f))

//loop through all entries in the zipped file
var entry = zis.nextEntry
while(entry != null) {
    val baos = ByteArrayOutputStream()

    //read the entry into a ByteArrayOutputStream
    zis.use{ it.copyTo(baos) }

    val bytes = baos.toByteArray()

    System.out.println(bytes[0])

    zis.closeEntry()  //error thrown here on first iteration
    entry = zis.nextEntry
}

Я получаю ошибку:

java.io.IOException: Stream closed
    at java.util.zip.ZipInputStream.ensureOpen(ZipInputStream.java:67)
    at java.util.zip.ZipInputStream.closeEntry(ZipInputStream.java:139)
    <the code above>

Я думал, может быть zis.use закрывает запись уже после прочтения ее содержимого, поэтому я удалил zis.closeEntry(), но затем выдает ту же ошибку при попытке получить следующую запись

я знаю zis.use является безопасным и гарантирует, что входной поток закрыт, но я ожидаю, что он закроет только запись, а не весь поток.

Распечатав весь байтовый массив, я знаю, что только первый файл в zip читается во время zis.use

Есть ли хороший способ прочитать все записи в ZipInputStream в kotlin?

3 ответа

Решение

use Функция вызывает метод close(), который закрывает весь поток, а не closeEntry (), который закрывает только текущую запись. Я думаю, что вы должны обернуть все while цикл с zis.use { ... }вместо того, чтобы вызывать его для каждой записи.

Есть ли хороший способ прочитать все записи в ZipInputStream в котлине?

Вот функция для извлечения файлов из Zip-файла, которую вы можете использовать в качестве основы и настраивать под свои нужды:

      data class UnzippedFile(val filename: String, val content: ByteArray)

fun unzip(file: File): List<UnzippedFile> = ZipInputStream(FileInputStream(file))
    .use { zipInputStream ->
        generateSequence { zipInputStream.nextEntry }
            .filterNot { it.isDirectory }
            .map {
                UnzippedFile(
                    filename = it.name,
                    content = zipInputStream.readAllBytes()
                )
            }.toList()
    }

Ключевым моментом является использование <tcode id="17691568"></tcode> для обработки итерации по записям, пока не останется ни одной.

Пример использования, извлечение zip-архива, содержащего три текстовых файла в каталоге:

      fun main() {
    val unzipped = unzip(File("zipped.zip"))
    for ((filename, content) in unzipped) {
        println("Contents of $filename: '${content.toString(Charset.defaultCharset()).trim()}'")
    }
}

Выход:

      Contents of zipped/two.txt: 'contents of two'
Contents of zipped/three.txt: 'three!!!!'
Contents of zipped/one.txt: 'contents of one'

В Котлине use закроет ресурс, который реализует AutoCloseable, Это означает, что его close() метод вызывается для вас автоматически. Я думаю, что вы предполагаете, что в ZipInputStreamЭто было отменено, чтобы закрыть только записи, но это не так.

Согласно документации:

Закрывает этот входной поток и освобождает все системные ресурсы, связанные с этим потоком. [Акцент мой]

Другие вопросы по тегам