3

Please have a look at the below code.

public void startCompress(String path,String fileName,String outputLocation,int compressType,int filSize) throws Exception
    {        
        System.out.println("Input Location: "+path);
        System.out.println("Output Location: "+outputLocation);   

            System.out.println(compressType);
            byte[] bs=new byte[filSize];
            System.out.println(filSize);

            FileOutputStream fos=new FileOutputStream(outputLocation+"/test.zip");

            System.out.println(fos.toString());
            ZipOutputStream zos=new ZipOutputStream(fos);

            ZipEntry ze = new ZipEntry(fileName);

            zos.putNextEntry(ze);

            FileInputStream inputStream=new FileInputStream(path);

            int len;
            while((len=inputStream.read(bs))>0){
                zos.write(bs, 0, len);                
            }
            inputStream.close();
            zos.closeEntry();
            zos.close();

    }

In above code, we compress a file using java.util.zip package. But we have an issue. That is, if we select multiple files then only one file is being compressed. If we select a folder, the compression simply won't work.

How can I fix this to compress either a file, files, folder, folders, or even nested folders? Java zip package does support .zip, .tar, .tarGz and tarZ. So the solution should not be something which is limited to .zip extension as well.

PeakGen
  • 21,894
  • 86
  • 261
  • 463
  • Then pass it a list of files...something like [this](http://stackoverflow.com/questions/16381281/creating-folders-in-a-zip-folder-in-java/16381391#16381391) o [this](http://stackoverflow.com/questions/28732118/java-zip-file-with-non-static-filename/28732297#28732297) for example – MadProgrammer May 12 '15 at 06:44
  • 1
    http://www.mkyong.com/java/how-to-compress-files-in-zip-format/ And also http://stackoverflow.com/questions/15968883/how-to-zip-a-folder-itself-using-java – Sercan Ozdemir May 12 '15 at 06:45
  • @SercanOzdemir: His example differs, we have to identfy whether the selected is a file or folder as well.. – PeakGen May 12 '15 at 06:46
  • @MadProgrammer: Glad you are here. Can you help me with some code sample? – PeakGen May 12 '15 at 06:46
  • @JustCause Take a look at the two linked examples (updated comment) – MadProgrammer May 12 '15 at 06:47
  • @MadProgrammer: Allright, I will check. – PeakGen May 12 '15 at 06:47
  • @MadProgrammer: Tested the second example, but it creates folders for every single file inside a folder! – PeakGen May 12 '15 at 07:24
  • @JustCause What do you mean? It creates folders for each file in the zip file or when your extract it? – MadProgrammer May 12 '15 at 07:28
  • @MadProgrammer: When In extracted it, every file inside it had a folder. I zipped a folder with images, every single image was inside its own folder when I extract it. – PeakGen May 12 '15 at 07:31
  • @JustCause Cheers, I went back and looked at and there was a "potential" bug, based on the needs of the user. I corrected it so the files are now "grouped" into directories better – MadProgrammer May 12 '15 at 07:41
  • @MadProgrammer: Amazing, thank you. I will check. Anyway, how to compress "multiple files" into one? – PeakGen May 12 '15 at 07:50
  • @MadProgrammer: Hi, it still creates folder for files.. – PeakGen May 12 '15 at 09:04
  • @JustCause Yeah, that's what it's suppose to do, otherwise you could end up with duplicate zip entries. If you don't want to maintain the path information, you always comment out the section which generates the path and simply keep the file name as the entry name – MadProgrammer May 12 '15 at 09:38
  • @MadProgrammer:nono. That is not what I meant. Imagine I selected a "folder" named "Folder". Now after compressing this, when I extract it, the uncompressed version should contain "Folder" folder. But instead, it contains everything inside the root folder, but no root folder. – PeakGen May 12 '15 at 10:04
  • Ah, so the example code is stripping off the "source" path. You should be able to change `path = path.substring(sourcePath.length());` to something like `path = path.substring(sourceFile.getParentFile().length());` – MadProgrammer May 12 '15 at 11:05
  • If anyone does use the mkYong example, the line `FileInputStream in = new FileInputStream(SOURCE_FOLDER + File.separator + file);` should be replaced with `FileInputStream in = new FileInputStream(SOURCE_FOLDER + "/" + file);` as the windows "\" is not valid for zip format, failure to do that will result in the root directory off the zip file being and underscore, e.g. "_" - that was some fun debugging! – JGlass Apr 04 '19 at 20:45

3 Answers3

2

The zip libraries for java cannot be used to compress folders in simpler way like - compress this folder.

You need to do the test if the input is folder or file by yourself. If it is a file - add it to the zip. If it is a folder - iterate the folder and add each file to the zip. For the subfolders to the same. To add more than one file to the Zip you need to create ZipEntry for each file.

You can try this code which works for me:

public static void zip(File directory, File zipfile) throws IOException {
    URI base = directory.toURI();
    Deque<File> queue = new LinkedList<File>();
    queue.push(directory);
    OutputStream out = new FileOutputStream(zipfile);
    Closeable res = out;
    try {
        ZipOutputStream zout = new ZipOutputStream(out);
        res = zout;
        while (!queue.isEmpty()) {
            directory = queue.pop();
            for (File kid : directory.listFiles()) {
                String name = base.relativize(kid.toURI()).getPath();
                if (kid.isDirectory()) {
                    queue.push(kid);
                    name = name.endsWith("/") ? name : name + "/";
                    zout.putNextEntry(new ZipEntry(name));
                } else {
                    zout.putNextEntry(new ZipEntry(name));
                    copy(kid, zout);
                    zout.closeEntry();
                }
            }
        }
    } finally {
        res.close();
    }
}
Veselin Davidov
  • 7,031
  • 1
  • 15
  • 23
2

Here is my solution that uses the new java.nio package. Just call zipDir giving it the path to the directory. It will create a zip file in the same location but called <directory>.zip.

private static Path buildPath(final Path root, final Path child) {
    if (root == null) {
        return child;
    } else {
        return Paths.get(root.toString(), child.toString());
    }
}

private static void addZipDir(final ZipOutputStream out, final Path root, final Path dir) throws IOException {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
        for (Path child : stream) {
            Path entry = buildPath(root, child.getFileName());
            if (Files.isDirectory(child)) {
                addZipDir(out, entry, child);
            } else {
                out.putNextEntry(new ZipEntry(entry.toString()));
                Files.copy(child, out);
                out.closeEntry();
            }
        }
    }
}

public static void zipDir(final Path path) throws IOException {
    if (!Files.isDirectory(path)) {
        throw new IllegalArgumentException("Path must be a directory.");
    }

    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path.toString() + ".zip"));

    try (ZipOutputStream out = new ZipOutputStream(bos)) {
        addZipDir(out, path.getFileName(), path);
    }
}
Mr Lister
  • 45,515
  • 15
  • 108
  • 150
crowmagnumb
  • 6,621
  • 9
  • 33
  • 42
1

Updated from this answer, which fixes issue with each file been added to it's own directory. Also better supports Windows explorer.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Test {

    public static void main(String agrs[]) {
        ZipUtils appZip = new ZipUtils();
        appZip.zipIt(new File(source directory), new File(dest zip));
    }

    public static class ZipUtils {

        private final List<File> fileList;

        private List<String> paths;

        public ZipUtils() {
            fileList = new ArrayList<>();
            paths = new ArrayList<>(25);
        }

        public void zipIt(File sourceFile, File zipFile) {
            if (sourceFile.isDirectory()) {
                byte[] buffer = new byte[1024];
                FileOutputStream fos = null;
                ZipOutputStream zos = null;

                try {

                    // This ensures that the zipped files are placed
                    // into a folder, within the zip file
                    // which is the same as the one been zipped
                    String sourcePath = sourceFile.getParentFile().getPath();
                    generateFileList(sourceFile);

                    fos = new FileOutputStream(zipFile);
                    zos = new ZipOutputStream(fos);

                    System.out.println("Output to Zip : " + zipFile);
                    FileInputStream in = null;

                    for (File file : this.fileList) {
                        String path = file.getParent().trim();
                        path = path.substring(sourcePath.length());

                        if (path.startsWith(File.separator)) {
                            path = path.substring(1);
                        }

                        if (path.length() > 0) {
                            if (!paths.contains(path)) {
                                paths.add(path);
                                ZipEntry ze = new ZipEntry(path + "/");
                                zos.putNextEntry(ze);
                                zos.closeEntry();
                            }
                            path += "/";
                        }

                        String entryName = path + file.getName();
                        System.out.println("File Added : " + entryName);
                        ZipEntry ze = new ZipEntry(entryName);

                        zos.putNextEntry(ze);
                        try {
                            in = new FileInputStream(file);
                            int len;
                            while ((len = in.read(buffer)) > 0) {
                                zos.write(buffer, 0, len);
                            }
                        } finally {
                            in.close();
                        }
                    }

                    zos.closeEntry();
                    System.out.println("Folder successfully compressed");

                } catch (IOException ex) {
                    ex.printStackTrace();
                } finally {
                    try {
                        zos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        protected void generateFileList(File node) {

// add file only
            if (node.isFile()) {
                fileList.add(node);

            }

            if (node.isDirectory()) {
                File[] subNote = node.listFiles();
                for (File filename : subNote) {
                    generateFileList(filename);
                }
            }
        }
    }

}
Community
  • 1
  • 1
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366