11

I have list of files arranges in different directories, some directories will have sub-directories and files in it. I am not able to successfully put same directory structure within a zip file. Here is my code:

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

 ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use {
       zipFiles(it, sourceFile)
   }
}

private fun zipFiles(zipOut: ZipOutputStream, directory: File) {

val data = ByteArray(1024)

zipOut.use {

    if (directory.isDirectory) {
        //Adding directory
        it.putNextEntry(ZipEntry(directory.name))
    } else {
        zipFiles(zipOut, directory)
    }

    for (f in directory.listFiles()) {

        if (!f.name.contains(".zip") && f.exists()) {

            //Adding file

            FileInputStream(f).use { fi ->
                BufferedInputStream(fi).use { origin ->
                    val entry = ZipEntry(f.name)
                    it.putNextEntry(entry)
                    while (true) {
                        val readBytes = origin.read(data)
                        if (readBytes == -1) {
                            break
                        }
                        it.write(data, 0, readBytes)
                    }
                }
            }
        }
    }
}
}
Umair Adil
  • 968
  • 1
  • 10
  • 24

4 Answers4

15

It's a bit old question, but I think I have a better solution, here you are:

    val inputDirectory = File("/home/fred")
    val outputZipFile = File.createTempFile("out", ".zip")
    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().use { fis -> fis.copyTo(zos) }
            }
        }
    }

You can filter out some files in forEach loop by test 'file' variable (for example filter out zip files to prevent it from compressing)

Alexey Sviridov
  • 3,360
  • 28
  • 33
6

I solved it, here is complete solution:

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

    ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use {
        it.use {
            zipFiles(it, sourceFile, "")
        }
    }
}

private fun zipFiles(zipOut: ZipOutputStream, sourceFile: File, parentDirPath: String) {

    val data = ByteArray(2048)

    for (f in sourceFile.listFiles()) {

        if (f.isDirectory) {
            val entry = ZipEntry(f.name + File.separator)
            entry.time = f.lastModified()
            entry.isDirectory
            entry.size = f.length()

            Log.i("zip", "Adding Directory: " + f.name)
            zipOut.putNextEntry(entry)

            //Call recursively to add files within this directory
            zipFiles(zipOut, f, f.name)
        } else {

            if (!f.name.contains(".zip")) { //If folder contains a file with extension ".zip", skip it
                FileInputStream(f).use { fi ->
                    BufferedInputStream(fi).use { origin ->
                        val path = parentDirPath + File.separator + f.name
                        Log.i("zip", "Adding file: $path")
                        val entry = ZipEntry(path)
                        entry.time = f.lastModified()
                        entry.isDirectory
                        entry.size = f.length()
                        zipOut.putNextEntry(entry)
                        while (true) {
                            val readBytes = origin.read(data)
                            if (readBytes == -1) {
                                break
                            }
                            zipOut.write(data, 0, readBytes)
                        }
                    }
                }
            } else {
                zipOut.closeEntry()
                zipOut.close()
            }
        }
    }
}

Usage:

zipAll("Path of source files to Zip", "Path for Zip to Export")
Umair Adil
  • 968
  • 1
  • 10
  • 24
  • 1
    This creates somwhat broken zip files when files are packed which arent in a subfolder. Than the filename startes with a file seperator which breaks multiple programs. Solution is to replace one line with ´val path = if(parentDirPath.isEmpty()) f.name else parentDirPath + File.separator + f.name´ – Henning Mar 03 '23 at 19:27
1

This is my complete solution. It don't ignore zip unlike Umair Adil version and also respect folder hierarchy like Jeyhey patch

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

    ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use {
        zipFiles(it, sourceFile, "")
    }
}

private fun zipFiles(zipOut: ZipOutputStream, sourceFile: File, parentDirPath: String) {
    val data = ByteArray(2048)
    sourceFile.listFiles()?.forEach { f ->
        if (f.isDirectory) {
            val path = if (parentDirPath == "") {
                f.name
            } else {
                parentDirPath + File.separator + f.name
            }
            val entry = ZipEntry(path + File.separator)
            entry.time = f.lastModified()
            entry.isDirectory
            entry.size = f.length()
            zipOut.putNextEntry(entry)
            //Call recursively to add files within this directory
            zipFiles(zipOut, f, path)
        } else {
            FileInputStream(f).use { fi ->
                BufferedInputStream(fi).use { origin ->
                    val path = parentDirPath + File.separator + f.name
                    val entry = ZipEntry(path)
                    entry.time = f.lastModified()
                    entry.isDirectory
                    entry.size = f.length()
                    zipOut.putNextEntry(entry)
                    while (true) {
                        val readBytes = origin.read(data)
                        if (readBytes == -1) {
                            break
                        }
                        zipOut.write(data, 0, readBytes)
                    }
                }
            }
        }
    }
}
Dasser Basyouni
  • 3,142
  • 5
  • 26
  • 50
Gonzo Oin
  • 369
  • 2
  • 12
  • There is possibly a problem in this script, some files are not compressed and the script gets stuck. This also happens to Umair Adil version. – Pedro Paulo Amorim Aug 04 '20 at 10:55
0

This zips nested folders into a flat directory structure. If you want to keep the nested structure, use the following code in the isDirectory-if-case:

if (f.isDirectory) {
  val path = if (parentDirPath == "") {
    f.name
  } else {
    parentDirPath + File.separator + f.name
  }
  val entry = ZipEntry(path + File.separator)
  entry.time = f.lastModified()
  entry.isDirectory
  entry.size = f.length()
  zipOut.putNextEntry(entry)
  //Call recursively to add files within this directory
  zipFiles(zipOut, f, path)
} else {
Jeyhey
  • 490
  • 3
  • 16