All Downloads are FREE. Search and download functionalities are using the official Maven repository.

goja.core.kits.io.ZipKit Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2013-2014 sagyf Yang. The Four Group.
 */

package goja.core.kits.io;

import goja.core.StringPool;
import goja.core.kits.base.CharKit;
import goja.core.kits.base.Wildcard;
import goja.core.kits.stream.StreamUtil;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * Performs zip/gzip/zlib operations on files and directories.
 * These are just tools over existing java.util.zip classes,
 * meaning that existing behavior and bugs are persisted.
 * Most common issue is not being able to use UTF8 in file names,
 * because implementation uses old ZIP format that supports only
 * IBM Code Page 437. This bug was resolved in JDK7:
 * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4244499
 */
public class ZipKit {

    public static final String ZIP_EXT  = ".zip";
    public static final String GZIP_EXT = ".gz";
    public static final String ZLIB_EXT = ".zlib";

    // ---------------------------------------------------------------- deflate

    /**
     * Compresses a file into zlib archive.
     */
    public static void zlib(String file) throws IOException {
        zlib(new File(file));
    }

    /**
     * Compresses a file into zlib archive.
     */
    public static void zlib(File file) throws IOException {
        if (file.isDirectory()) {
            throw new IOException("Can't zlib folder");
        }
        FileInputStream fis = new FileInputStream(file);
        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
        DeflaterOutputStream dos = new DeflaterOutputStream(new FileOutputStream(file.getAbsolutePath() + ZLIB_EXT), deflater);
        try {
            StreamUtil.copy(fis, dos);
        } finally {
            StreamUtil.close(dos);
            StreamUtil.close(fis);
        }
    }

    // ---------------------------------------------------------------- gzip

    /**
     * Compresses a file into gzip archive.
     */
    public static void gzip(String fileName) throws IOException {
        gzip(new File(fileName));
    }

    /**
     * Compresses a file into gzip archive.
     */
    public static void gzip(File file) throws IOException {
        if (file.isDirectory()) {
            throw new IOException("Can't gzip folder");
        }
        FileInputStream fis = new FileInputStream(file);
        GZIPOutputStream gzos = new GZIPOutputStream(new FileOutputStream(file.getAbsolutePath() + GZIP_EXT));
        try {
            StreamUtil.copy(fis, gzos);
        } finally {
            StreamUtil.close(gzos);
            StreamUtil.close(fis);
        }
    }

    /**
     * Decompress gzip archive.
     */
    public static void ungzip(String file) throws IOException {
        ungzip(new File(file));
    }

    /**
     * Decompress gzip archive.
     */
    public static void ungzip(File file) throws IOException {
        String outFileName = FilenameUtils.removeExtension(file.getAbsolutePath());
        File out = new File(outFileName);
        out.createNewFile();

        FileOutputStream fos = new FileOutputStream(out);
        GZIPInputStream gzis = new GZIPInputStream(new FileInputStream(file));
        try {
            StreamUtil.copy(gzis, fos);
        } finally {
            StreamUtil.close(fos);
            StreamUtil.close(gzis);
        }
    }

    // ---------------------------------------------------------------- zip

    /**
     * Zips a file or a folder.
     *
     * @see #zip(java.io.File)
     */
    public static void zip(String file) throws IOException {
        zip(new File(file));
    }

    /**
     * Zips a file or a folder. If adding a folder, all its content will be added.
     */
    public static void zip(File file) throws IOException {
        String zipFile = file.getAbsolutePath() + ZIP_EXT;

        ZipOutputStream zos = null;
        try {
            zos = createZip(zipFile);
            addToZip(zos).file(file).recursive().add();
        } finally {
            StreamUtil.close(zos);
        }
    }

    // ---------------------------------------------------------------- unzip

    /**
     * Extracts zip file content to the target directory.
     *
     * @see #unzip(java.io.File, java.io.File, String...)
     */
    public static void unzip(String zipFile, String destDir, String... patterns) throws IOException {
        unzip(new File(zipFile), new File(destDir), patterns);
    }

    /**
     * Extracts zip file to the target directory. If patterns are provided
     * only matched paths are extracted.
     *
     * @param zipFile  zip file
     * @param destDir  destination directory
     * @param patterns optional wildcard patterns of files to extract, may be null
     */
    public static void unzip(File zipFile, File destDir, String... patterns) throws IOException {
        ZipFile zip = new ZipFile(zipFile);
        Enumeration zipEntries = zip.entries();

        while (zipEntries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) zipEntries.nextElement();
            String entryName = entry.getName();

            if (patterns != null && patterns.length > 0) {
                if (Wildcard.matchPathOne(entryName, patterns) == -1) {
                    continue;
                }
            }

            File file = (destDir != null) ? new File(destDir, entryName) : new File(entryName);
            if (entry.isDirectory()) {
                if (!file.mkdirs()) {
                    if (!file.isDirectory()) {
                        throw new IOException("Failed to create directory: " + file);
                    }
                }
            } else {
                File parent = file.getParentFile();
                if (parent != null && !parent.exists()) {
                    if (!parent.mkdirs()) {
                        if (!file.isDirectory()) {
                            throw new IOException("Failed to create directory: " + parent);
                        }
                    }
                }

                InputStream in = zip.getInputStream(entry);
                OutputStream out = null;
                try {
                    out = new FileOutputStream(file);
                    StreamUtil.copy(in, out);
                } finally {
                    StreamUtil.close(out);
                    StreamUtil.close(in);
                }
            }
        }

        close(zip);
    }

    // ---------------------------------------------------------------- zip stream

    /**
     * Creates and opens zip output stream of a zip file.
     *
     * @see #createZip(java.io.File)
     */
    public static ZipOutputStream createZip(String zipFile) throws FileNotFoundException {
        return createZip(new File(zipFile));
    }

    /**
     * Creates and opens zip output stream of a zip file. If zip file exist it will be recreated.
     */
    public static ZipOutputStream createZip(File zip) throws FileNotFoundException {
        return new ZipOutputStream(new FileOutputStream(zip));
    }

    /**
     * Starts a command for adding file entries to the zip.
     *
     * @see #addToZip(java.util.zip.ZipOutputStream, java.io.File, String, String, boolean)
     */
    public static AddToZip addToZip(ZipOutputStream zos) {
        return new AddToZip(zos);
    }

    /**
     * Adds single entry to ZIP output stream. For user-friendly way of adding entries to zip
     * see {@link #addToZip(java.util.zip.ZipOutputStream)}.
     *
     * @param zos       zip output stream
     * @param file      file or folder to add
     * @param path      relative path of file entry; if null files name will be used instead
     * @param comment   optional comment
     * @param recursive when set to true content of added folders will be added, too
     */
    public static void addToZip(ZipOutputStream zos, File file, String path, String comment, boolean recursive) throws IOException {
        if (!file.exists()) {
            throw new FileNotFoundException(file.toString());
        }

        if (path == null) {
            path = file.getName();
        }

        while (path.length() != 0 && path.charAt(0) == '/') {
            path = path.substring(1);
        }

        boolean isDir = file.isDirectory();

        if (isDir) {
            // add folder record
            if (!CharKit.endsWithChar(path, '/')) {
                path += '/';
            }
        }

        ZipEntry zipEntry = new ZipEntry(path);
        zipEntry.setTime(file.lastModified());

        if (comment != null) {
            zipEntry.setComment(comment);
        }

        if (isDir) {
            zipEntry.setSize(0);
            zipEntry.setCrc(0);
        }

        zos.putNextEntry(zipEntry);

        if (!isDir) {
            InputStream is = new FileInputStream(file);
            try {
                StreamUtil.copy(is, zos);
            } finally {
                StreamUtil.close(is);
            }
        }

        zos.closeEntry();

        // continue adding

        if (recursive && file.isDirectory()) {
            boolean noRelativePath = StringUtils.isEmpty(path);

            final File[] children = file.listFiles();

            if (children != null && children.length != 0) {
                for (File child : children) {
                    String childRelativePath = (noRelativePath ? StringPool.EMPTY : path) + child.getName();
                    addToZip(zos, child, childRelativePath, comment, recursive);
                }
            }
        }

    }

    /**
     * Closes zip file safely.
     */
    public static void close(ZipFile zipFile) {
        if (zipFile != null) {
            try {
                zipFile.close();
            } catch (IOException ioex) {
                // ignore
            }
        }
    }

    // ---------------------------------------------------------------- close

    /**
     * Command: "add to zip".
     */
    public static class AddToZip {
        private final ZipOutputStream zos;
        private       File            file;
        private       String          path;
        private       String          comment;
        private boolean recursive = true;

        private AddToZip(ZipOutputStream zos) {
            this.zos = zos;
        }

        /**
         * Defines file or folder to be added to zip.
         */
        public AddToZip file(File file) {
            this.file = file;
            return this;
        }

        /**
         * Defines file or folder to be added to zip.
         */
        public AddToZip file(String fileName) {
            this.file = new File(fileName);
            return this;
        }

        /**
         * Defines file or folder to be added to zip.
         */
        public AddToZip file(String parent, String child) {
            this.file = new File(parent, child);
            return this;
        }

        /**
         * Defines optional entry path.
         */
        public AddToZip path(String path) {
            this.path = path;
            return this;
        }

        /**
         * Defines optional comment.
         */
        public AddToZip comment(String comment) {
            this.comment = comment;
            return this;
        }

        /**
         * Defines if folders content should be added.
         * Ignored for files.
         */
        public AddToZip recursive() {
            this.recursive = true;
            return this;
        }

        /**
         * Invokes the adding command.
         */
        public void add() throws IOException {
            addToZip(zos, file, path, comment, recursive);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy