如何在 Kotlin 中使用 ZipOutputStream 压缩文件夹和子文件夹及其中的文件?

Uma*_*dil 11 android kotlin

我有排列在不同目录中的文件列表,某些目录中会有子目录和文件。我无法成功地将相同的目录结构放入 zip 文件中。这是我的代码:

fun zipAll(directory: String, zipFile: String) {
val sourceFile = File(directory)

 ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use {
       zipFiles(it, sourceFile)
   }
}

private fun zipFiles(zipOut: ZipOutputStream, directory: File) {

val data = ByteArray(1024)

zipOut.use {

    if (directory.isDirectory) {
        //Adding directory
        it.putNextEntry(ZipEntry(directory.name))
    } else {
        zipFiles(zipOut, directory)
    }

    for (f in directory.listFiles()) {

        if (!f.name.contains(".zip") && f.exists()) {

            //Adding file

            FileInputStream(f).use { fi ->
                BufferedInputStream(fi).use { origin ->
                    val entry = ZipEntry(f.name)
                    it.putNextEntry(entry)
                    while (true) {
                        val readBytes = origin.read(data)
                        if (readBytes == -1) {
                            break
                        }
                        it.write(data, 0, readBytes)
                    }
                }
            }
        }
    }
}
}
Run Code Online (Sandbox Code Playgroud)

Ale*_*dov 15

这是一个有点老的问题,但我想我有更好的解决方案,在这里:

    val inputDirectory = File("/home/fred")
    val outputZipFile = File.createTempFile("out", ".zip")
    ZipOutputStream(BufferedOutputStream(FileOutputStream(outputZipFile))).use { zos ->
        inputDirectory.walkTopDown().forEach { file ->
            val zipFileName = file.absolutePath.removePrefix(inputDirectory.absolutePath).removePrefix("/")
            val entry = ZipEntry( "$zipFileName${(if (file.isDirectory) "/" else "" )}")
            zos.putNextEntry(entry)
            if (file.isFile) {
                file.inputStream().use { fis -> fis.copyTo(zos) }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

您可以通过测试 'file' 变量过滤掉 forEach 循环中的一些文件(例如过滤掉 zip 文件以防止其压缩)


Uma*_*dil 6

我解决了,这是完整的解决方案:

fun zipAll(directory: String, zipFile: String) {
    val sourceFile = File(directory)

    ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use {
        it.use {
            zipFiles(it, sourceFile, "")
        }
    }
}

private fun zipFiles(zipOut: ZipOutputStream, sourceFile: File, parentDirPath: String) {

    val data = ByteArray(2048)

    for (f in sourceFile.listFiles()) {

        if (f.isDirectory) {
            val entry = ZipEntry(f.name + File.separator)
            entry.time = f.lastModified()
            entry.isDirectory
            entry.size = f.length()

            Log.i("zip", "Adding Directory: " + f.name)
            zipOut.putNextEntry(entry)

            //Call recursively to add files within this directory
            zipFiles(zipOut, f, f.name)
        } else {

            if (!f.name.contains(".zip")) { //If folder contains a file with extension ".zip", skip it
                FileInputStream(f).use { fi ->
                    BufferedInputStream(fi).use { origin ->
                        val path = parentDirPath + File.separator + f.name
                        Log.i("zip", "Adding file: $path")
                        val entry = ZipEntry(path)
                        entry.time = f.lastModified()
                        entry.isDirectory
                        entry.size = f.length()
                        zipOut.putNextEntry(entry)
                        while (true) {
                            val readBytes = origin.read(data)
                            if (readBytes == -1) {
                                break
                            }
                            zipOut.write(data, 0, readBytes)
                        }
                    }
                }
            } else {
                zipOut.closeEntry()
                zipOut.close()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

zipAll("Path of source files to Zip", "Path for Zip to Export")
Run Code Online (Sandbox Code Playgroud)

  • 当打包不在子文件夹中的文件时,这会创建一些损坏的 zip 文件。文件名以文件分隔符开头,这会破坏多个程序。解决方案是将一行替换为“val path = if(parentDirPath.isEmpty()) f.name elseparentDirPath + File.separator + f.name” (2认同)