Create a ZIP file in Kotlin

Viewed 14749

I'm trying to create a zip file in Kotlin. this is the code:

fun main(args: Array<String>) {
var files: Array<String> = arrayOf("/home/matte/theres_no_place.png", "/home/matte/vladstudio_the_moon_and_the_ocean_1920x1440_signed.jpg")
var out = ZipOutputStream(BufferedOutputStream(FileOutputStream("/home/matte/Desktop/test.zip")))
var data = ByteArray(1024)
for (file in files) {
    var fi = FileInputStream(file)
    var origin = BufferedInputStream(fi)
    var entry = ZipEntry(file.substring(file.lastIndexOf("/")))
    out.putNextEntry(entry)
    origin.buffered(1024).reader().forEachLine {
        out.write(data)
    }
    origin.close()
}
out.close()}

the zip file is created, but the files inside are corrupt!

7 Answers

Here is a solution working with subfolders:

fun addFolderToZip(
    folder: String,
    destination: String,
    zipFileName: String = folder.substring(folder.lastIndexOf("/"))
) {

    val folderToZip = File(folder)
    var out: ZipOutputStream? = null
    try {
        out = ZipOutputStream(
            BufferedOutputStream(FileOutputStream("$destination/$zipFileName"))
        )
        recursivelyAddZipEntries(folderToZip, folderToZip.absolutePath, out)
    } catch (e: Exception) {
        Log.e("ZIP Err", e.message)
    } finally {
        out?.close()
    }

}


private fun recursivelyAddZipEntries(
    folder: File,
    basePath: String,
    out: ZipOutputStream
) {

    val files = folder.listFiles() ?: return
    for (file in files) {

        if (file.isDirectory) {
            recursivelyAddZipEntries(file, basePath, out)
        } else {
            val origin = BufferedInputStream(FileInputStream(file))
            origin.use {
                val entryName = file.path.substring(basePath.length)
                out.putNextEntry(ZipEntry(entryName))
                origin.copyTo(out, 1024)
            }
        }

    }

}

I'm not sure if you want to do it manually but I found this nice library that works perfectly:

https://github.com/zeroturnaround/zt-zip

This library is a nice wrapper of the Java Zip Utils library that include methods for zipping/unzipping both files and directories with a single function.

For zipping a single file you just need to use the packEntry method:

ZipUtil.packEntry(File("/tmp/demo.txt"), File("/tmp/demo.zip"))

For the case of zipping a directory and its sub-directories you can use the pack method:

val dirToCompress = Paths.get("/path/to/my/dir").toFile()
val targetOutput = Paths.get("/output/path/dir.zip").toFile()

ZipUtil.pack(dirToCompress, targetOutput)

The zip file should have been created in the specified target output.

You can find more details and examples in the library's documentation.

Hope this helps =)

This is a simpler solution, also provided by https://stackoverflow.com/a/63828765/3792198

fun test() {

        val fullPath: String = tempFolder.absolutePath // Folder to be zipped
        val zipFilePath = File(baseDirectory, "newTest.zip")// new zip file

        zipAll(fullPath, zipFilePath.absolutePath)

}




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

    println("directory: $directory")
    println("zipFile: $zipFile")

    val inputDirectory = sourceFile
    val outputZipFile = File(zipFile)

    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().copyTo(zos)
            }
        }
    }
}

The code can be cleaned up a bit to separate concerns and take a better advantage of use:

fun File.bufferedOutputStream(size: Int = 8192) = BufferedOutputStream(this.outputStream(), size)
fun File.zipOutputStream(size: Int = 8192) = ZipOutputStream(this.bufferedOutputStream(size))
fun File.bufferedInputStream(size: Int = 8192) = BufferedInputStream(this.inputStream(), size)
fun File.asZipEntry() = ZipEntry(this.name)

fun archive(files: List<File>, destination: File) =
    destination.zipOutputStream().use {
        files.forEach { file ->
            it.putNextEntry(file.asZipEntry())
            file.bufferedInputStream().use { bis -> bis.copyTo(it) }
        }
    }


fun main() {
    val files = listOf(
        File("/Users/xor/Downloads/Ghibli/kaguyahime006.jpg"),
        File("/Users/xor/Downloads/Ghibli/kaguyahime035.jpg")
    )

    val destination = File("/Users/xor/work/kotlin/scratchpad-kotlin-java/src/main/kotlin/main/archive.zip")

    archive(files, destination)
}
Related