How to Read Files from a Zip File in Java

Naveen AnamaneniUpdated · 4 min read

Reading a zip file means looking at the entries inside an archive and, if needed, reading or extracting their contents. Java offers two classes in java.util.zip. ZipFile opens a zip file on disk and lets you list and read entries in any order. ZipInputStream reads a zip as a stream, one entry after another, which also works for zips arriving over the network. In this lesson, you will learn both, print every file in an archive, and extract files safely.

Classes and Methods

Class / methodDescription
new ZipFile(file)It is used to open a zip file for random access to its entries.
zipFile.stream() / entries()It returns all entries.
zipFile.getInputStream(entry)It reads one entry's contents.
new ZipInputStream(in)It reads a zip sequentially from any stream.
zin.getNextEntry()It moves to the next entry, or returns null at the end.
entry.getName(), getSize(), isDirectory()It describes an entry.

Example: Listing and Printing Entries with ZipFile

Let's see an example that creates a small zip and prints every file in it.

ReadZip.java▶ Try it
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.TreeMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
 
public class ReadZip {
    public static void main(String[] args) throws IOException {
        Map<String, String> files = new TreeMap<>(Map.of(
            "class/navika.txt", "Marks: 91",
            "class/advitha.txt", "Marks: 85"));
        try (OutputStream f = Files.newOutputStream(Path.of("class.zip"));
             ZipOutputStream out = new ZipOutputStream(f)) {
            for (var e : files.entrySet()) {
                out.putNextEntry(new ZipEntry(e.getKey()));
                out.write(e.getValue().getBytes(StandardCharsets.UTF_8));
                out.closeEntry();
            }
        }
 
        try (ZipFile zip = new ZipFile("class.zip")) {
            System.out.println(zip.size() + " entries");
            for (ZipEntry entry : zip.stream().toList()) {
                try (InputStream in = zip.getInputStream(entry)) {
                    String text = new String(in.readAllBytes(),
                                             StandardCharsets.UTF_8);
                    System.out.println(entry.getName() + " (" + entry.getSize()
                        + " bytes): " + text);
                }
            }
        }
    }
}
Output
2 entries
class/advitha.txt (9 bytes): Marks: 85
class/navika.txt (9 bytes): Marks: 91

Explanation: The first part builds a zip with two entries. ZipFile then opens it, stream() lists the entries, and getInputStream(entry) reads each one. getSize() gives the uncompressed size.

Example: Extracting Safely with ZipInputStream

Extract.java▶ Try it
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
 
public class Extract {
    public static void main(String[] args) throws IOException {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        try (ZipOutputStream out = new ZipOutputStream(bytes)) {
            String[] names = {"notes/srihitha.txt", "../evil.txt"};
            for (String name : names) {
                out.putNextEntry(new ZipEntry(name));
                out.write("hello".getBytes());
                out.closeEntry();
            }
        }
 
        Path target = Files.createDirectories(Path.of("unzipped"))
                           .toAbsolutePath().normalize();
        try (ZipInputStream in = new ZipInputStream(
                new ByteArrayInputStream(bytes.toByteArray()))) {
            ZipEntry entry;
            while ((entry = in.getNextEntry()) != null) {
                Path dest = target.resolve(entry.getName()).normalize();
                if (!dest.startsWith(target)) {
                    System.out.println("Blocked: " + entry.getName());
                    continue;
                }
                Files.createDirectories(dest.getParent());
                Files.copy(in, dest);
                System.out.println("Extracted: " + target.relativize(dest));
            }
        }
    }
}
Output
Extracted: notes/srihitha.txt
Blocked: ../evil.txt

Explanation: getNextEntry moves through the archive one entry at a time, and Files.copy(in, dest) copies the current entry's bytes. Before writing, each destination is normalized and checked to be inside the target folder. The entry named ../evil.txt would escape the folder, so it is blocked. This attack is called zip slip.

Common Mistakes

Extracting entry names without checking them. A crafted name can overwrite files outside the target.

Using ZipFile for a zip that is still downloading. Use ZipInputStream for streams.

Trusting getSize(). It can be -1 when unknown; read until the end of the entry instead.

Practice

  1. Easy: List the names and sizes of all entries in a zip file you create.
  2. Harder: Write unzip(Path zip, Path target) that extracts every entry safely, creating folders as needed and skipping unsafe names.

Quick check: Reading zip files

4 questions
1Which class reads entries in any order from a zip on disk?
2What does getNextEntry() return at the end?
3What is zip slip?
4How do you check an extracted path is safe?

Key Takeaways

Was this tutorial helpful?

Lesson 364 of 482