About Rafal Borowiec

Rafal is an IT specialist with about 8 years of commercial experience, specializing in software testing and quality assurance, software development, project management and team leadership.

Listing a ZIP file contents with Stream API in Java 8

In Java 8 java.util.zip.ZipFile was equipped with a stream method that allows navigating over a ZIP file entries very easily. In this blog post I will show a bunch of examples showing how quickly we can navigate over ZIP file entries.

Note: For the purpose of this blog post I downloaded one of my GitHub repositories as a ZIP file and I copied it to c:/tmp.

Prior to Java 7

Reading ZIP file entries in Java prior to Java 7 is a kind of hmm… tricky? This is how one can start hating Java while looking at this code:

public class Zipper {
    public void printEntries(PrintStream stream, String zip)  {
        ZipFile zipFile = null;
        try {
            zipFile = new ZipFile(zip);
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry zipEntry = entries.nextElement();
                stream.println(zipEntry.getName());
            }
        } catch (IOException e) {
            // error while opening a ZIP file
        } finally {
            if (zipFile != null) {
                try {
                    zipFile.close();
                } catch (IOException e) {
                    // do something
                }
            }
        }
    }
}

Java 7

With Java 7 the same can be much simpler – thanks to try-with-resources but we are still “forced” to use Enumeration in order to navigate over ZIP file entries:

public class Zipper {
    public void printEntries(PrintStream stream, String zip) {
        try (ZipFile zipFile = new ZipFile(zip)) {
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry zipEntry = entries.nextElement();
                stream.println(zipEntry.getName());
            }
        } catch (IOException e) {
            // error while opening a ZIP file
        }
    }
}

Using Stream API

The real fun starts with Java 8. As of Java 8 java.util.zip.ZipFile has a new method stream that returns an ordered stream over the ZIP file entries. This gives many opportunities while working with ZIP files in Java. Previous examples can be simply written as follows in Java 8:

public class Zipper {
    public void printEntries(PrintStream stream, String zip) {
        try (ZipFile zipFile = new ZipFile(zip)) {
            zipFile.stream()
                    .forEach(stream::println);
        } catch (IOException e) {
            // error while opening a ZIP file
        }
    }
}

With Stream API we can play with the ZipFile in many ways. See below…

Filtering and sorting ZIP file contents

public void printEntries(PrintStream stream, String zip) {
    try (ZipFile zipFile = new ZipFile(zip)) {
        Predicate<ZipEntry> isFile = ze -> !ze.isDirectory();
        Predicate<ZipEntry> isJava = ze -> ze.getName().matches(".*java");
        Comparator<ZipEntry> bySize = 
                (ze1, ze2) -> Long.valueOf(ze2.getSize() - ze1.getSize()).intValue();
        zipFile.stream()
                .filter(isFile.and(isJava))
                .sorted(bySize)
                .forEach(ze -> print(stream, ze));
    } catch (IOException e) {
        // error while opening a ZIP file
    }
}

private void print(PrintStream stream, ZipEntry zipEntry) {
    stream.println(zipEntry.getName() + ", size = " + zipEntry.getSize());
}

While iterating over ZIP entries, I check if the entry is a file and if it matches a given name (harcoded in this example, for sake of simlicity) and then I sort it by size using a given comparator.

Create files index of a ZIP file

In this example I group ZIP entries by first letter of a file name to create Map<String, List<ZipEntry>> index. The expected result should look similar to the below one:

a = [someFile/starting/with/an/A]
u = [someFile/starting/with/an/U, someOtherFile/starting/with/an/U]

Again, with Stream API it is really easy:

public void printEntries(PrintStream stream, String zip) {
    try (ZipFile zipFile = new ZipFile(zip)) {
        Predicate<ZipEntry> isFile = ze -> !ze.isDirectory();
        Predicate<ZipEntry> isJava = ze -> ze.getName().matches(".*java");
        Comparator<ZipEntry> bySize =
            (ze1, ze2) -> Long.valueOf(ze2.getSize()).compareTo(Long.valueOf(ze1.getSize()));

        Map<String, List<ZipEntry>> result = zipFile.stream()
                .filter(isFile.and(isJava))
                .sorted(bySize)
                .collect(groupingBy(this::fileIndex));

        result.entrySet().stream().forEach(stream::println);

    } catch (IOException e) {
        // error while opening a ZIP file
    }
}

private String fileIndex(ZipEntry zipEntry) {
    Path path = Paths.get(zipEntry.getName());
    Path fileName = path.getFileName();
    return fileName.toString().substring(0, 1).toLowerCase();
}

Find a text within a ZIP file entry

In the last example, I search for a @Test text occurrence in all files with java extension. This time I will utilize BufferedReader’s lines method that returns a stream of lines.

public void printEntries(PrintStream stream, String zip) {

    try (ZipFile zipFile = new ZipFile(zip)) {
        Predicate<ZipEntry> isFile = ze -> !ze.isDirectory();
        Predicate<ZipEntry> isJava = ze -> ze.getName().matches(".*java");

        List<ZipEntry> result = zipFile.stream()
                .filter(isFile.and(isJava))
                .filter(ze -> containsText(zipFile, ze, "@Test"))
                .collect(Collectors.toList());

        result.forEach(stream::println);


    } catch (IOException e) {
        // error while opening a ZIP file
    }
}

private boolean containsText(ZipFile zipFile, ZipEntry zipEntry, String needle) {
    try (InputStream inputStream = zipFile.getInputStream(zipEntry);
         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {

        Optional<String> found = reader.lines()
                .filter(l -> l.contains(needle))
                .findFirst();

        return found.isPresent();

    } catch (IOException e) {
        return false;
    }
}

Summary

Stream API in Java 8 is kind a powerful solution that helps in solving relatively easy tasks easily. And that’s its power, in my opinion.

The examples presented in this article are relatively simple and they were created for visualization purpose only. But I hope you like them and find them useful.

Resources

Reference: Listing a ZIP file contents with Stream API in Java 8 from our JCG partner Rafal Borowiec at the Codeleak.pl blog.
Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

Leave a Reply


+ one = 9



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books