Reading and writing ZIP files in Java

You can read and write ZIP files in Java using the classes ZipInputStream and ZipOutputStream. Both use ZipEntry for navigating the zipped files within the archive. These classes belong to the package java.util.zip and are part of Java’s standard library. Here is how to use them.

In this example we will develop methods for reading and writing a zip archive directly from Java. We will do that step by step. After that I will give you the complete Java code.

Preparation

First we need some zip file that we would like to read. Use your favorite zip tool to generate one. You can use the Windows Explorer or you can use some zip tool like 7-zip (from https://www.7-zip.org/Opens in a new tab.).

Create a zip archive with some content like

  • somefile.txt
  • dir/someotherfile.txt
  • dir/emptysubdir

How to read a zip file’s directory

Let’s start easy and create a class ZipFileExample. Now let us explore how to read the zip file’s directory. In a later step we will also access the content of these files.

Create a method

public static void readZipDirectory(String filename) throws IOException {
}

filename is the name of the zip file that we would like to read.

In principle we can encounter all kinds of errors. So the method will throw an exeption when the file cannot be read. It might not exist. Or it might exist, but you do not have the neccessary access rights. You might start reading the file and then the network connection breaks because of a power failure at your Internet provider. The file might be corrupted. Whatever. Remember: whenever you access files within Java, the underlying Java methods might throw an exception. So you need to think about what to do when that happens. You can pass the exception up to the user. You might handle it in some way within your program. But ignoring an exception is rarely a good choice. Here we just pass the exception up.

Now let’s open the zip file as a Java stream so that we can start reading the file.

        // the zip file
        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        BufferedInputStream bfis = new BufferedInputStream(fis);
        ZipInputStream zipis = new ZipInputStream(bfis);

Things will also work without the BufferedInputStream, but it is a good practise to use one. You will need it, when the files you want to handle are larger.

Access to the files that are contained in the zip archive happens by using the class ZipEntry. The zip stream does not have an iterator. You need to use the old-fashioned while-loop. Get one ZipEntry after the other, until you are done.

        ZipEntry zipentry;
        int numEntry = 0;
        while ((zipentry = zipis.getNextEntry()) != null) {
            numEntry++;
            if ( zipentry.isDirectory()) {
                System.out.print("Directory ");
            }
            else {
                System.out.print("File ");
            }
            System.out.format("Entry #%d: path=%s, size=%d, compressed size=%d \n", numEntry, zipentry.getName(), zipentry.getSize(), zipentry.getCompressedSize(), );
        }
        // remember to close the zip input stream!
        zipis.close();

Each zip entry can tell you, whether it is a file or a directory. It knows the original size and the compressed size. Your IDE will tell you some more methods (e.g. for accessing the timestamp).

When you are done, remember to close the zip stream.

So write a main program, pass some file name to readZipDirectory and try it out.

My output looks like this:

Directory Entry #1: path=dir/, size=0, compressed size=0
Directory Entry #2: path=dir/emptysubdir/, size=0, compressed size=0
File Entry #3: path=dir/someotherfile.txt, size=37, compressed size=35
File Entry #4: path=somefile.txt, size=25, compressed size=25

Here is the complete method:

// preconditions: generate a zip file with some content like
// somefile.txt
// dir/someotherfile.txt
// dir/emptysubdir
    public static void readZipDirectory(String filename) throws IOException {
        // the zip file
        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        BufferedInputStream bfis = new BufferedInputStream(fis);
        ZipInputStream zipis = new ZipInputStream(bfis);

        ZipEntry zipentry;
        int numEntry = 0;
        while ((zipentry = zipis.getNextEntry()) != null) {
            numEntry++;
            if ( zipentry.isDirectory()) {
                System.out.print("Directory ");
            }
            else {
                System.out.print("File ");
            }
            System.out.format("Entry #%d: path=%s, size=%d, compressed size=%d\n", numEntry, zipentry.getName(), zipentry.getSize(), zipentry.getCompressedSize());
        }
        // remember to close the zip input stream!
        zipis.close();
    }

How to read a zip file’s content

Now let us extend this example to accessing the file contents. Copy the method to readZipContents

The code to accessing the file contents looks like this:

            byte[] buffer = new byte[2048];
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len;
            // read bytes of file
            while ( (len = zis.read(buffer)) > 0 ) {
                System.out.println( "file: " + zipEntry.getName() + " : " + len + "  bytes read");
                bos.write(buffer,0,len );
            }
            // convert bytes to string
            byte[] zipFileBytes = bos.toByteArray();
            String fileContent = new String ( zipFileBytes );
            System.out.println("Content of file: " + zipEntry.getName() + " : " + fileContent );

Here we already get the uncompressed data. The zip output stream handels the decompression for you.

We need a buffer to efficiently read the file content. Blockwise is better than one byte after the other. We are working in bytes. Raw file access in Java likes using byte arrays. So by providing a buffer array of 2048 bytes, we can read 2048 bytes each time.

We keep reading bytes of the file. Every time Java will tell us, how many bytes were read. This will not always be 2048. It can be less. Every call returns how many bytes were passed to the buffer. Then we collect these bytes in a ByteArrayOutputStream.

When the file has been read completely, we ask the output buffer to give us the complete file content, again as bytes. We convert this into as String as this likely will be the format you want to use.

For simplicity we just write the content to stdout. But here you can do whatever you want to accomplish with your data. It could e.g. be an XML that you want to parse.

If the file is an image file, converting to String might not be such a bright idea. In that case check what format your other tools further downstream would like to see.

As promised above, here is the complete example:

    public static void readZipContents(String filename) throws IOException {
        // the zip file
        File f = new File(filename);
        FileInputStream fis = new FileInputStream(f);
        BufferedInputStream bfis = new BufferedInputStream(fis);
        ZipInputStream zis = new ZipInputStream(bfis);

        ZipEntry zipEntry;
        int numEntry = 0;
        while ((zipEntry = zis.getNextEntry()) != null) {
            numEntry++;
            System.out.format("Entry #%d: path=%s, size=%d, compressed size=%d \n", numEntry, zipEntry.getName(), zipEntry.getSize(), zipEntry.getCompressedSize());
            byte[] buffer = new byte[2048];
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            int len;
            // read bytes of file
            while ( (len = zis.read(buffer)) > 0 ) {
                System.out.println( "file: " + zipEntry.getName() + " : " + len + "  bytes read");
                bos.write(buffer,0,len );
            }
            // convert bytes to string
            byte[] zipFileBytes = bos.toByteArray();
            String fileContent = new String ( zipFileBytes );
            System.out.println("Content of file: " + zipEntry.getName() + " : " + fileContent );
            
        }
        // remember to close the zip input stream!
        zis.close();
    }

How to write a zip file

Java can also write a zip file. So where does your input come from? You can use files from your file system. So you can write a program that takes a directory, walks through the file list, reads each file and puts it into a zip archive.

What I want to accomplish is something else. I would like to generate a zip package for uploading an integration flow into a SAP CPI. So my Java program will provide all file names and their contents directly from within Java. I will not first write all those files to disk and then pack them. I want to directly write them into the ZIP file.

The important class is ZipOutputStream. So let us open that stream and write it to disk. For this we need a File, a FileOutputStream, a BufferedOutputStream for performance (I have not tried it without. If you do, you are on your own.) and a ZipOutputStream.

Then let’s provide some filenames and some content. For our little example here, we will just come up with some simple strings. In a real program, some other class or method will provide those file contents and maybe also the names.

As we want to write several files, let’s delegate the actual file writing to a method writeSingleFileToZip that we can use several times.

    public static void writeZipFile(String zipFilename)
            throws IOException {

        File f = new File(zipFilename);
        FileOutputStream fos = new FileOutputStream(f);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        ZipOutputStream zos = new ZipOutputStream(bos);
        // write these files:

        String filename = "sometextfile.txt";
        String content = "Here is some text";
        writeSingleFileToZip(zos, filename, content);

        filename = "subdir/somemoretext.txt";
        content = "Here is some more text";
        writeSingleFileToZip(zos, filename, content);

        zos.close();
        
        System.out.println("zip file " + zipFilename + " written");
    }

Finally we close the zip output stream.

By the way: you do not have to close the zip file. Closing the zip output stream already closes the file.

And here is how to fill the file contents into the zip archive.

Please note that again we have to provide bytes. So we convert the String to a byte array.

For each file we generate a new ZipEntry and tell the zip stream that we have a new file. Then we write the content array into the stream. The signature requires to specify, how many bytes are written.

When we are done, we close the zip entry (not the zip output stream). The zip output stream remains open for the next file.

    private static void writeSingleFileToZip(ZipOutputStream zos, String filename, String content) throws IOException {
        System.out.println("Writing file " + filename + " to ZIP archive ");
        // we need the content as bytes, not as a String
        byte[] bytes = content.getBytes();

        // start new entry
        ZipEntry zipEntry = new ZipEntry(filename);
        zos.putNextEntry(zipEntry);

        // write content to ZIP output stream, awkward method signature.
        zos.write(bytes, 0, bytes.length);
        // don't forget to close the entry!
        zos.closeEntry();
    }

Have fun with zip files in Java

So now you know how to pack and unpack zip files in Java. As you can see, this is surprisingly easy. It does not require packing and unpacking on the file system. You can do all that directly in memory.

Have fun and do something awesome with it. Like generating zip packages for SAP CPI.

More java stuff can be found hereOpens in a new tab..

Recent Posts