CodeSharpMarvin
CodeSharpMarvin

Reputation: 153

Trouble with using ZipInputStream.getNextEntry() multiple times

I am trying to unzip specific files from a zip. I first get a ZipInputStream:

ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(filePath)));

Alright, this works! Now I want to extract two files, called F1 and F2, so I call

extractFileFromZip(zipIn, Path + "F1", "F1")
extractFileFromZip(zipIn, Path + "F2", "F2")

public static boolean extractFileFromZip(ZipInputStream inZip, String file, String name) throws Exception {
    byte data[] = new byte[BUFFER_SIZE];

    boolean found = false;

    ZipEntry ze;
    while((ze = inZip.getNextEntry()) != null) {
        if(ze.getName().equals(name)) {
            found = true;
            // delete old file first
            File oldFile = new File(file);
            if(oldFile.exists()) {
                if(!oldFile.delete()) {
                    throw new Exception("Could not delete " + file);
                }
            }

            FileOutputStream outFile = new FileOutputStream(file);
            int count = 0;
            while((count = inZip.read(data)) != -1) {
                outFile.write(data, 0, count);
            }

            outFile.close();
            //inZip.closeEntry();
        }
    }
    return true;
}

Now the problem is with inZip.getNextEntry(). For F1 it will cycle through all the files correctly and then give null. But for F2, it will just give null.

Why does this happen?

Upvotes: 0

Views: 2845

Answers (2)

Andreas
Andreas

Reputation: 159135

You're scanning the entire stream, consuming it. When you try to do it the second time, the stream is already at-end, so will do nothing.

Also, streaming all bytes in the zip file is slow, if you only want a small part of it.

Use a ZipFile instead, since it allows random access to the zip entries, so it is faster, and it allows reading the entries in random order.

Note: The code below has been changed to use Java 7+ features for better error handling, such as try-with-resources and NIO.2.

ZipFile zipFile = new ZipFile(filePath);
extractFileFromZip(zipFile, path + "F1", "F1");
extractFileFromZip(zipFile, path + "F2", "F2");

public static boolean extractFileFromZip(ZipFile zipFile, String file, String name) throws IOException {
    ZipEntry ze = zipFile.getEntry(name);
    if (ze == null)
        return false;
    Path path = Paths.get(file);
    Files.deleteIfExists(path);
    try (InputStream in = zipFile.getInputStream(ze)) {
        Files.copy(in, path);
    }
    return true;
}

Alternatively, stream it only once, then check for both names in the while loop.

Map<String, String> nameMap = new HashMap<>();
nameMap.put("F1", path + "F1");
nameMap.put("F2", path + "F2");
extractFilesFromZip(filePath, nameMap);

public static void extractFilesFromZip(String filePath, Map<String, String> nameMap) throws IOException {
    try (ZipInputStream zipIn = new ZipInputStream(new BufferedInputStream(new FileInputStream(filePath)))) {
        for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null; ) {
            String file = nameMap.get(ze.getName());
            if (file != null) {
                Path path = Paths.get(file);
                Files.deleteIfExists(path);
                Files.copy(zipIn, path);
            }
        }
    }
}

Upvotes: 1

user207421
user207421

Reputation: 310980

Because you read the entire stream to extract F1. So next time, for F2, getNextEntry() returned null. You need to either reopen the zip stream, or, better still, pass a list of files to unzip to your method.

Upvotes: 0

Related Questions