How to compress and decompress zip file in Kotlin

In this tutorial, we’ll explore how to create ZIP files, add multiple files and folders to a zip file, add files to an and decompress files using Kotlin.

Table of contents:

1. Adding Multiple Files to a ZIP File

Below example shows how to create a ZIP file and add multiple files with existence checking:

ZipFileExample.kt

import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

// Function to create ZIP file and add multiple files
fun zipFiles(files: List<String>, zipFileName: String) {
    // Create a ZipOutputStream to write the ZIP file
    ZipOutputStream(FileOutputStream(zipFileName)).use { zipOut ->
        files.forEach { filePath ->
            val fileToZip = File(filePath)
            if (!fileToZip.exists()) throw FileNotFoundException("File not found: ${fileToZip.path}")
            FileInputStream(fileToZip).use { fis ->
                // Create a new ZipEntry for each file
                val zipEntry = ZipEntry(fileToZip.name)
                zipOut.putNextEntry(zipEntry)
                // Copy file content into the ZIP entry
                fis.copyTo(zipOut)
                zipOut.closeEntry()
            }
        }
    }
}

fun main() {
    val filesToZip = listOf("output.txt", "readme.md", "play.pdf")
    zipFiles(filesToZip, "archive.zip")
    println("ZIP file created successfully!")
}

2. Adding Multiple Folders to a ZIP File

When adding multiple folders, it’s important to preserve folder paths to prevent duplicate entry errors:

ZipFolderExample.kt

import java.io.*
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream

// Function to add folders and their contents to a ZIP file
fun zipFolders(folders: List<String>, zipFileName: String) {
    ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFileName))).use { zos ->
        folders.forEach { folderPath ->
            val folder = File(folderPath)
            if (!folder.exists() || !folder.isDirectory) 
                throw FileNotFoundException("Folder not found or not a directory: $folderPath")
            // Traverse each folder recursively
            folder.walkTopDown().forEach { file ->
                if (file.isFile) {
                    // Include the folder name in entry to avoid duplicates
                    // Get the relative path for each file
                    val entryName = folder.name + "/" + folder.toURI().relativize(file.toURI()).path
                    FileInputStream(file).use { fis ->
                        val entry = ZipEntry(entryName)
                        zos.putNextEntry(entry)
                        fis.copyTo(zos)
                        zos.closeEntry()
                    }
                }
            }
        }
    }
}

fun main() {
    val foldersToZip = listOf("folder1", "folder2")
    zipFolders(foldersToZip, "folders.zip")
    println("Folders zipped successfully!")
}

3. Adding Multiple Files or Directories to an Existing ZIP File

To add multiple files or directories to an existing ZIP, we copy existing entries to a temporary ZIP file, add new entries, and then replace the original ZIP file:

AddItemsToZipExample.kt

import java.io.*
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream

// Function to add a file to an existing ZIP file
fun addFileToZip(existingZip: String, newFiles: List<File>) {
    // Create a temporary ZIP file for intermediate storage
    val tempFile = File.createTempFile("tempZip", ".zip")

    ZipOutputStream(BufferedOutputStream(FileOutputStream(tempFile))).use { zos ->
        // Copy existing entries from original ZIP to the temporary ZIP
        ZipInputStream(BufferedInputStream(FileInputStream(existingZip))).use { zis ->
            var entry = zis.nextEntry
            while (entry != null) {
                zos.putNextEntry(ZipEntry(entry.name))
                zis.copyTo(zos)
                zos.closeEntry()
                entry = zis.nextEntry
            }
        }

        newFiles.forEach { file ->
            if (file.isDirectory) {
                // add directories
                file.walkTopDown().forEach { innerFile ->
                    if (innerFile.isFile) {
                        val entryName = file.name + "/" + file.toURI().relativize(innerFile.toURI()).path
                        FileInputStream(innerFile).use { fis ->
                            zos.putNextEntry(ZipEntry(entryName))
                            fis.copyTo(zos)
                            zos.closeEntry()
                        }
                    }
                }
            } else {
                // add files
                FileInputStream(file).use { fis ->
                    zos.putNextEntry(ZipEntry(file.name))
                    fis.copyTo(zos)
                    zos.closeEntry()
                }
            }
        }

    }
    // Replace the original ZIP file with the updated temporary ZIP
    File(existingZip).delete()
    tempFile.renameTo(File(existingZip))
}

fun main() {
    val existingZip = "existing.zip"
    val newItems = listOf(File("newfile.txt"), File("newfolder"))
    addFileToZip(existingZip, newItems)
    println("Items added to ZIP successfully!")
}

4. Decompressing (Extracting) a ZIP File

Extracting files from a ZIP archive in Kotlin, and also prevent the Zip Slip Vulnerability.

UnZipFolderExample.kt

package com.mkyong.wpbackend.test

import java.io.BufferedInputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.ZipInputStream

// Function to unzip a ZIP file to a specified destination directory
fun unzip(zipFilePath: String, destDir: String) {

    val destDirFile = File(destDir).canonicalFile // Resolve the destination folder path securely

    ZipInputStream(BufferedInputStream(FileInputStream(zipFilePath))).use { zis ->
        var entry = zis.nextEntry
        while (entry != null) {

            val newFile = File(destDirFile, entry.name).canonicalFile

            // Prevent Zip Slip: Ensure the newFile is within the intended directory
            if (!newFile.toPath().startsWith(destDirFile.toPath())) {
                throw SecurityException("Blocked Zip Slip attack attempt: ${entry.name}")
            }

            if (entry.isDirectory) {
                newFile.mkdirs()
            } else {
                newFile.parentFile.mkdirs()
                FileOutputStream(newFile).use { fos -> zis.copyTo(fos) }
            }
            zis.closeEntry()
            entry = zis.nextEntry
        }
    }
}

fun main() {
    unzip("folders.zip", "outputFolder")
    //unzip("archive.zip", "outputFolder")
    println("Files extracted successfully!")
}

5. Kotlin vs. Java: Working with ZIP Files

Working with ZIP files in Kotlin is very similar to Java, but Kotlin simplifies the syntax and improves readability.

Here’s a quick comparison:

Java:


try (ZipOutputStream zos = new ZipOutputStream(
        new BufferedOutputStream(
                new FileOutputStream("files.zip")))) {
    FileInputStream fis = new FileInputStream("file.txt");
    zos.putNextEntry(new ZipEntry("file.txt"));
    byte[] bytes = new byte[1024];
    int length;
    while ((length = fis.read(bytes)) >= 0) {
        zos.write(bytes, 0, length);
    }
    zos.closeEntry();
    fis.close();
}

Kotlin:


ZipOutputStream(BufferedOutputStream(FileOutputStream("files.zip"))).use { zos ->
    FileInputStream("file.txt").use { fis ->
        zos.putNextEntry(ZipEntry("file.txt"))
        fis.copyTo(zos)
        zos.closeEntry()
    }
}

6. References

mkyong

Founder of Mkyong.com, passionate Java and open-source technologies. If you enjoy my tutorials, consider making a donation to these charities.

0 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments