Unzip.java

/*
 * Copyright (c) 2021 by k3b.
 *
 * This file is part of k3b-geoHelper library.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package de.k3b.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class Unzip {
    public static final String TAG = Unzip.class.getSimpleName();
    private static final Logger LOGGER = LoggerFactory.getLogger(TAG);

    // see https://stackoverflow.com/questions/1809007/best-way-to-detect-if-a-stream-is-zipped-in-java/68038770#68038770
    public static boolean isZipStream(InputStream inputStream) {
        if (inputStream == null || !inputStream.markSupported()) {
            throw new IllegalArgumentException("InputStream must support mark-reset. Use BufferedInputstream()");
        }
        boolean isZipped = false;
        try {
            inputStream.mark(64);
            isZipped = new ZipInputStream(inputStream).getNextEntry() != null;
            inputStream.reset();
        } catch (IOException ex) {
            // cannot be opend as zip.
        }
        return isZipped;
    }

    public static void unzip(String name, InputStream inputStream, File destinationDir) throws IOException {
        ZipInputStream zipInputStream = null;
        String message = "unzip('" + name +
                "' => '" + destinationDir + "') ";
        LOGGER.info( message);

        try {
            zipInputStream = new ZipInputStream(inputStream);

            for (ZipEntry zipEntry = zipInputStream.getNextEntry(); zipEntry != null; zipEntry = zipInputStream
                    .getNextEntry()) {
                unzipEntry(zipInputStream, zipEntry, destinationDir);
            }
        } catch (IOException e) {
            LOGGER.error( "err " + message + e.getMessage(), e);
            throw new IOException(message, e);
        } finally {
            if (zipInputStream != null) zipInputStream.close();
        }
    }

    private static void unzipEntry(ZipInputStream zipInputStream, ZipEntry entry, File outputDir) throws IOException {
        if (entry.isDirectory()) {
            createDir(new File(outputDir, entry.getName()));
            return;
        }

        File outputFile = new File(outputDir, entry.getName());
        if (!outputFile.getParentFile().exists()) {
            createDir(outputFile.getParentFile());
        }

        String message = "unzipEntry(" + entry + ")[" + entry.getSize() + "] ";
        LOGGER.debug( message);

        OutputStream output = null;
        try {
            output = new FileOutputStream(outputFile);
            copy(zipInputStream, output);
        } catch (IOException e) {
            LOGGER.error( "err " + message + e.getMessage(), e);
            throw new IOException(message, e);
        } finally {
            // keep zipInputStream open for next entry-file
            if (output != null) output.close();
        }

    }

    private static void createDir(File dir) {
        if (dir.exists()) {
            return;
        }
        LOGGER.debug("Creating dir " + dir.getName());

        if (!dir.mkdirs()) {
            throw new RuntimeException("Can not create dir " + dir);
        }
    }

    private static void copy(InputStream input, OutputStream output) throws IOException {
        byte data[] = new byte[10240];
        int count;

        int total = 0;
        while ((count = input.read(data)) != -1) {
            output.write(data, 0, count);
            total += count;
        }
        output.flush();
    }

    public static void deleteRecursive(File f) {
        if (f != null && f.exists()) {
            if (f.isDirectory()) {
                for (File c : f.listFiles()) {
                    deleteRecursive(c);
                }
            }
            f.delete();
        }
    }
}