javazipjava-iooutputstreamzipoutputstream

ZipOutputStream does not create new entries for Directories


I am trying to create directories in a .zip file but only one directory is being created in the .zip. I'm not sure why only the first directory gets created. zip() method gets called everytime theres a directory.

Method to list files:

private Set<String> listFiles(String path){

        File f = new File(path);
        Set<String> files = new HashSet<>();

        for(File file : Objects.requireNonNull(f.listFiles())){
            files.add(file.getPath());
        }
        return files;
    }

Method to zip:

 private void zip(Set<String> path){
        try {
            BufferedInputStream bufferedInputStream = null;
            BufferedOutputStream bufferedOutputStream;
            ZipOutputStream zipOutputStream;

            File f;

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

                bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("/sdcard/Android/data/com.kpwnapps.pmmpplugins/files/PocketMine-MP/test.zip"));
                zipOutputStream= new ZipOutputStream(bufferedOutputStream, StandardCharsets.UTF_8);

                for (String file : path){

                    f = new File(file);

                    if (f.isDirectory()){
                        zipOutputStream.putNextEntry(new ZipEntry(f.getName() + "/"));
                        zip(listFiles(f.getPath()));
                    }else {
                      
                        bufferedInputStream = new BufferedInputStream(new FileInputStream(file));
                        zipOutputStream.putNextEntry(new ZipEntry(f.getName()));
                        IOUtils.copy(bufferedInputStream, zipOutputStream);
                        zipOutputStream.closeEntry();
                    }
                     */

                }

                if (bufferedInputStream != null){
                    bufferedInputStream.close();
                }

                zipOutputStream.flush();
                zipOutputStream.close();

            }
        }catch (Exception ignored){
        }

    }

Solution

  • I'm mildly surprised that this even works.

    When your zip() method calls itself recursively it creates a new FileOutputStream with the same filename as the previous call. That means that your recursive calls write on top of each other instead of appending to a single zip file.

    You should open the FileOutputStream / ZipOutputStream only once and then use it for zipping all files and directories.

    That means rewriting some of your code so that at the first level of zipping you create the zipOutputStream and then call an internal method zip(path, zipOutputStream). The recursive calls also must call this zip(path, zipOutputStream) method.

    This will look something like

    private void zip(Set<String> path) {
        // create FileOutputStream, ZipOutputStream
        ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
        // call worker method
        zip(path, zipOutputStream);
        // close output stream
        zipOutputStream.flush();
        zipOutputStream.close();
    }
    
    private void zip(Set<String> path, ZipOutputStream zipOutputStream) {
        // loop over paths
            if (f.isDirectory()) {
                zipOutputStream.putNextEntry(new ZipEntry(f.getName() + "/"));
                zip(listFiles(f.getPath()), zipOutputStream);
            }
        // end of loop over paths
    }