Tagged: zip

unzip in java


I thought I do know how to unzip in Java. Here is the code I used to think that is OK.

/**
 * Unzip given <code>source</code> zip file's contents into specified
 * <code>target</code> directory.
 *
 * @param source source zip file
 * @param target target directory
 * @throws IOException if an I/O error occurs.
 */
public static void unzip(final File source, final File target)
    throws IOException {

    if (source == null) {
        throw new NullPointerException("null source");
    }

    if (!source.isFile()) {
        throw new IllegalArgumentException("source doesn't exist");
    }

    if (target == null) {
        throw new NullPointerException("null target");
    }

    if (!target.isDirectory()) {
        throw new IllegalArgumentException("target doesn't exist");
    }

    final ZipInputStream sourceStream =
        new ZipInputStream(new FileInputStream(source));
    try {
        final byte[] buffer = new byte[8192];
        ZipEntry sourceEntry = null;
        while (true) {

            sourceEntry = sourceStream.getNextEntry();

            if (sourceEntry == null) {
                break;
            }

            final File targetEntry =
                new File(target, sourceEntry.getName());

            if (sourceEntry.isDirectory()) {
                if (!targetEntry.isDirectory() && !targetEntry.mkdirs()) {
                    throw new IOException(
                        "failed to create target directory: "
                        + targetEntry.getPath());
                }
                sourceStream.closeEntry();
                continue;
            }

            final OutputStream targetStream =
                new FileOutputStream(targetEntry);
            try {
                int read = -1;
                while (true) {
                    read = sourceStream.read(buffer);
                    if (read == -1) {
                        break;
                    }
                    targetStream.write(buffer, 0, read);
                }
                targetStream.flush();
            } finally {
                targetStream.close();
            }
            sourceStream.closeEntry();
        }
    } finally {
        sourceStream.close();
    }
}

And I encountered the evil.zip file which has following entries.

file
dir/
dir/file
dir/dir/file

See that there is no entry named dir/dir/ between the 3rd and the 4th entry. This case make above method to throw a FileNotFoundException that can’t open the file output stream for dir/dir/file because of the absence of dir/dir/ directory.
I’m not sure about the actual validity of the zip file. (Somebody tell me.) So I decided to insert some code to check if those required directories are exist or not.

/**
 * Unzip given <code>source</code> zip file's contents into specified
 * <code>target</code> directory.
 *
 * @param source source zip file
 * @param target target directory
 * @throws IOException if an I/O error occurs.
 */
public static void unzip(final File source, final File target)
    throws IOException {

    if (source == null) {
        throw new NullPointerException("null source");
    }

    if (!source.isFile()) {
        throw new IllegalArgumentException("source doesn't exist");
    }

    if (target == null) {
        throw new NullPointerException("null target");
    }

    if (!target.isDirectory()) {
        throw new IllegalArgumentException("target doesn't exist");
    }

    final ZipInputStream sourceStream =
        new ZipInputStream(new FileInputStream(source));
    try {
        final byte[] buffer = new byte[8192];
        ZipEntry sourceEntry = null;
        while (true) {

            sourceEntry = sourceStream.getNextEntry();

            if (sourceEntry == null) {
                break;
            }

            final File targetEntry =
                new File(target, sourceEntry.getName());

            if (sourceEntry.isDirectory()) {
                if (!targetEntry.isDirectory() && !targetEntry.mkdirs()) {
                    throw new IOException(
                        "failed to create target directory: "
                        + targetEntry.getPath());
                }
                sourceStream.closeEntry();
                continue;
            }

            final int index = sourceEntry.getName().lastIndexOf('/');
            if (index != -1) {
                final File parent = new File(
                    target, sourceEntry.getName().substring(0, index));
                if (!parent.isDirectory() && !parent.mkdirs()) {
                    throw new IOException(
                        "failed to create a directory: "
                        + parent.getPath());
                }
            }

            final OutputStream targetStream =
                new FileOutputStream(targetEntry);
            try {
                int read = -1;
                while (true) {
                    read = sourceStream.read(buffer);
                    if (read == -1) {
                        break;
                    }
                    targetStream.write(buffer, 0, read);
                }
                targetStream.flush();
            } finally {
                targetStream.close();
            }
            sourceStream.closeEntry();
        }
    } finally {
        sourceStream.close();
    }
}