如何使用 zip4j 和 outputstream 压缩文件夹和子文件夹

How to zip folder and sub-folder using zip4j and outputstream

在我正在编写的程序中,我获取了用户保存 zip 文件的位置的 uri。然后,我尝试使用 zip4j 库和 Android 中的 outpustream 压缩文件和文件夹。我修改了 this Whosebug answer 中的代码并改用 zip4j。我修改后的代码生成了 zip 文件,但它已损坏。

这是我用 Kotlin 编写的代码:

class ZipBuilder {
    private fun buildZipParameters(compressionMethod: CompressionMethod, compressionLevel: CompressionLevel,
                                   encrypt: Boolean,
                                   encryptionMethod: EncryptionMethod?,
                                   aesKeyStrength: AesKeyStrength?
    ): ZipParameters? {
        val zipParameters = ZipParameters()
        zipParameters.compressionMethod = compressionMethod
        zipParameters.compressionLevel = compressionLevel
        return zipParameters
    }


    fun zipFileAtPath(sourcePath: String?, toLocation: ParcelFileDescriptor?): Boolean {
        println("zipFileAtPath is called")
        val BUFFER = 2048
        val sourceFile = File(sourcePath!!)
        val zipParameters = buildZipParameters(CompressionMethod.DEFLATE, CompressionLevel.NORMAL,
            false, null, null)

        try {
            var origin: BufferedInputStream? = null
            val desc = toLocation
            val dest = FileOutputStream(desc!!.fileDescriptor)
            val out = ZipOutputStream(BufferedOutputStream(dest)) 

            if (sourceFile.isDirectory) {
                zipParameters.rootFolderNameInZip = sourcePath
                zipSubFolder(out, sourceFile, sourceFile.parent!!.length, zipParameters!!)
            } else {
                val data = ByteArray(BUFFER)
                val fi = FileInputStream(sourcePath)
                origin = BufferedInputStream(fi, BUFFER)

                zipParameters!!.fileNameInZip = getLastPathComponent(sourcePath)
                zipParameters.lastModifiedFileTime = sourceFile.lastModified()

                out.putNextEntry(zipParameters)

                var count: Int = 0
                while (fi.read(data).also({ count = it }) != -1) {
                    out.write(data, 0, count)
                }
            }
            out.close()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
            return false
        }
        return true
    }

    @Throws(IOException::class)
    private fun zipSubFolder(
        out: ZipOutputStream, folder: File, basePathLength: Int, zipParameters: ZipParameters
    ) {
        val BUFFER = 2048
        val fileList = folder.listFiles()
        var origin: BufferedInputStream
        fileList?.forEach { file ->
            if (file.isDirectory) {
                zipSubFolder(out, file, basePathLength, zipParameters)
            } else {
                val data = ByteArray(BUFFER)
                val unmodifiedFilePath = file.path
                val relativePath = unmodifiedFilePath
                    .substring(basePathLength)
                val fi = FileInputStream(unmodifiedFilePath)
                origin = BufferedInputStream(fi, BUFFER)


                zipParameters.fileNameInZip = relativePath
                zipParameters.lastModifiedFileTime = file.lastModified()
                out.putNextEntry(zipParameters)


                var count: Int = 0

                while (fi.read(data).also({ count = it }) != -1) {
                    out.write(data, 0, count)
                }
                origin.close()
            }
        }
    }

    fun getLastPathComponent(filePath: String): String? {
        val segments = filePath.split("/").toTypedArray()
        return if (segments.size == 0) "" else segments[segments.size - 1]
    }

}

如果有人能告诉我可能是什么问题,我将不胜感激。

我猜你忘记了 closeEntry() 在为该特定条目写下日期之后。你需要在写入数据的while循环之后做out.closeEntry()。看看例子 here.

while (fi.read(data).also({ count = it }) != -1) {
    out.write(data, 0, count)
}
out.closeEntry() // --> close entry after writing data