【问题标题】:Read ZipInputStream in Kotlin在 Kotlin 中读取 ZipInputStream
【发布时间】:2019-02-17 05:32:46
【问题描述】:

我正在尝试使用 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 是安全的并保证输入流已关闭,但我希望它只关闭条目而不是整个流。

打印了整个字节数组后,我知道在zis.use 期间只读取了 zip 中的第一个文件

有没有一种好方法可以读取 kotlin 中 ZipInputStream 中的所有条目?

【问题讨论】:

    标签: java kotlin zipinputstream


    【解决方案1】:

    有没有一种好方法可以读取 kotlin 中 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()
        }
    

    关键点是使用generateSequence 处理对条目的迭代,直到没有剩余。

    用法示例,解压一个目录下包含三个文本文件的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'
    

    【讨论】:

    • 这正是我想要的!
    【解决方案2】:

    use 函数调用 close() 方法,它关闭整个流,而不是 closeEntry(),它只关闭当前条目。我认为你应该用zis.use { ... } 包裹整个while 循环,而不是为每个条目调用它。

    【讨论】:

    • 谢谢。我知道我哪里出错了。我只看到通过将流包装在use 中来读取流的示例。我没有意识到 copyTo 方法实际上可以在任何输入流上调用,即使在 use 之外
    • @backcab 你能告诉我正确的代码是什么吗?
    • @androiddeveloper,不要做zis.use{ it.copyTo(baos) },只做zis.copyTo(baos)
    • @backcab 哦,好的。谢谢
    【解决方案3】:

    在 Kotlin 中,use 将关闭实现 AutoCloseable 的资源。这意味着它的close() 方法会自动为您调用。我认为您假设在ZipInputStream 中,它已被覆盖以仅关闭条目,但事实并非如此。

    根据the documentation

    关闭此输入流并释放与与该流相关的所有系统资源。 [强调我的]

    【讨论】:

      猜你喜欢
      • 2010-09-09
      • 2012-06-14
      • 2023-03-15
      • 1970-01-01
      • 2013-07-05
      • 2011-05-20
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多