'Create a ZIP file in Kotlin
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!
Solution 1:[1]
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 =)
Solution 2:[2]
If you use Kotlin's IOStreams.copyTo() extension, it will do the copying work for you, and that ended up working for me.
So replace this:
origin.buffered(1024).reader().forEachLine {
out.write(data)
}
With this:
origin.copyTo(out, 1024)
I also had issues with the ZipEntry having a leading slash, but that could just be because I'm on Windows.
Note: I didn't end up needing to call closeEntry() to get this to work but it is recommended.
Solution 3:[3]
I did a mix:
fun main(args: Array<String>) {
val files: Array<String> = arrayOf("/home/matte/theres_no_place.png", "/home/matte/vladstudio_the_moon_and_the_ocean_1920x1440_signed.jpg")
ZipOutputStream(BufferedOutputStream(FileOutputStream("/home/matte/Desktop/test.zip"))).use { out ->
for (file in files) {
FileInputStream(file).use { fi ->
BufferedInputStream(fi).use { origin ->
val entry = ZipEntry(file.substring(file.lastIndexOf("/")))
out.putNextEntry(entry)
origin.copyTo(out, 1024)
}
}
}
}
}
It works perfectly!
Solution 4:[4]
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)
}
}
}
}
Solution 5:[5]
1) You are writing an empty byte array to the out for each line of an input file.
2) There is no need in BufferedReader because it is enough to read and write bytes instead of lines (which would lead the unpacked content not to be matched with the original).
3) All streams should be closed in the case of exceptions. Use method use like try-with-resources in java.
4) val instead var there possible
5) Don't use absolute paths except for the quick test snippets.
6) This snippet is not in idiomatic way for Kotlin (see the Todd's answer)
So this is how it should work (though in the Java way):
fun main(args: Array<String>) {
val files: Array<String> = arrayOf("/home/matte/theres_no_place.png", "/home/matte/vladstudio_the_moon_and_the_ocean_1920x1440_signed.jpg")
ZipOutputStream(BufferedOutputStream(FileOutputStream("/home/matte/Desktop/test.zip"))).use { out ->
val data = ByteArray(1024)
for (file in files) {
FileInputStream(file).use { fi ->
BufferedInputStream(fi).use { origin ->
val entry = ZipEntry(file)
out.putNextEntry(entry)
while (true) {
val readBytes = origin.read(data)
if (readBytes == -1) {
break
}
out.write(data, 0, readBytes)
}
}
}
}
}
}
EDIT: I've ran this snippet with my files and it worked OK.
Solution 6:[6]
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)
}
}
}
}
Solution 7:[7]
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)
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Jhoan Manuel Muñoz Serrano |
| Solution 2 | Todd |
| Solution 3 | Brian Tompsett - æ±¤èŽ±æ© |
| Solution 4 | tochkov |
| Solution 5 | |
| Solution 6 | Thiago |
| Solution 7 |
