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
- 2. Adding Multiple Folders to a ZIP File
- 3. Adding Multiple Files or Directories to an Existing ZIP File
- 4. Decompressing (Extracting) a ZIP File
- 5. Kotlin vs. Java: Working with ZIP Files
- 6. References
1. Adding Multiple Files to a ZIP File
Below example shows how to create a ZIP file and add multiple files with existence checking:
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:
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:
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.
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()
}
}