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

org.cp.elements.util.zip.ZipUtils Maven / Gradle / Ivy

Go to download

Java Simplified. Extensions and Useful Constructs for the Java Platform. Codeprimate Elements (a.k.a. cp-elements) is a Java library and micro-framework used to simplify the development of software applications written in Java. Elements packages several APIs into one library in order to address various application concerns and aspects of software design and development collectively and conveniently. Elements is a highly simple, yet robust and proven library built on solid OO principles, software design patterns and best practices to effectively solve common and reoccurring problems in software development.

There is a newer version: 2.0.0-M1
Show newest version
/*
 * Copyright 2016 Author or Authors.
 *
 * 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 org.cp.elements.util.zip;

import static java.util.Arrays.stream;
import static org.cp.elements.lang.ElementsExceptionsFactory.newSystemException;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Optional;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.cp.elements.io.FileUtils;
import org.cp.elements.io.IOUtils;
import org.cp.elements.lang.Assert;
import org.cp.elements.lang.StringUtils;
import org.cp.elements.util.SystemException;

/**
 * The {@link ZipUtils} class is an abstract utility class for working with {@link File ZIP files}.
 *
 * @author John Blum
 * @see java.io.File
 * @see java.util.zip.ZipEntry
 * @see java.util.zip.ZipFile
 * @since 1.0.0
 */
@SuppressWarnings("unused")
public abstract class ZipUtils {

  public static final String ZIP_FILE_EXTENSION = ".zip";

  /**
   * Constructs a new {@link ZipEntry} from the given {@link File}.
   *
   * @param zipDirectory {@link File} referring to the directory being zipped.
   * @param file {@link File} used to construct the new {@link ZipEntry}.
   * @return a new {@link ZipEntry} constructed from the given {@link File}.
   * @throws IllegalArgumentException if {@link File} is {@literal null}.
   * @see #resolveZipEntryName(File, File)
   * @see java.util.zip.ZipEntry
   * @see java.io.File
   */
  protected static ZipEntry newZipEntry(File zipDirectory, File file) {

    Assert.notNull(file, "File is required");

    ZipEntry zipEntry = new ZipEntry(resolveZipEntryName(zipDirectory, file));

    zipEntry.setSize(file.getTotalSpace());
    zipEntry.setTime(file.lastModified());

    return zipEntry;
  }

  /**
   * Tries to resolve the {@link String} of the {@link ZipEntry} given the {@link File directory} being zipped
   * and the {@link File} from when the {@link ZipEntry} is being constructed.
   *
   * The resolved {@link ZipEntry} {@link String name} will be the relative path of the given {@link File}
   * if the {@link File} is relative to the given {@link File directory}, otherwise the {@link ZipEntry}
   * {@link String name} will be the {@link String name} of the given {@link File}.
   *
   * If {@link File diretory} is {@literal null}, or is not a valid {@link File directory},
   * or the {@link File directory} refers to the root of the file system (i.e. the {@link File#getName() name}
   * of the {@link File directory} is {@link String empty}), then the resolved {@link ZipEntry} {@link String name}
   * will be the {@link String name} of the given {@link File}.
   *
   * @param zipDirectory {@link File directory} being zipped.
   * @param file {@link File} used to construct the {@link ZipEntry}; must not be {@literal null}.
   * @return the resolved {@link String name} of the {@link ZipEntry}.
   * @see java.io.File
   */
  static String resolveZipEntryName(File zipDirectory, File file) {

    return Optional.ofNullable(zipDirectory)
      .filter(File::isDirectory)
      .map(File::getName)
      .filter(StringUtils::hasText)
      .map(zipDirectoryName -> {

        String filePath = file.getAbsolutePath();

        int index = filePath.indexOf(zipDirectoryName);

        return index > -1 ? filePath.substring(index) : null;
      })
      .orElseGet(file::getName);
  }

  /**
   * Unzips the given {@link File ZIP file} to the specified {@link File directory}.
   *
   * @param zip {@link File ZIP file} to unzip.
   * @param directory {@link File} referring to the file system path location in which to
   * unzip the {@link File ZIP file}.
   * @throws IllegalArgumentException if {@link File ZIP file} is {@literal null}
   * or the specified {@link File directory} is not a valid directory.
   * @throws IOException if an IO error occurs while reading the {@link File ZIP file}.
   * @throws SystemException if the {@link File ZIP file} could not be read or its contents unzipped.
   * @see java.util.zip.ZipFile
   * @see java.io.File
   */
  public static void unzip(File zip, File directory) throws IOException {

    Assert.notNull(zip, "ZIP file is required");

    Assert.isTrue(FileUtils.createDirectory(directory), String.format("[%s] is not a valid directory", directory));

    ZipFile zipFile = new ZipFile(zip, ZipFile.OPEN_READ);

    zipFile.stream().forEach(zipEntry -> {

      if (zipEntry.isDirectory()) {
        Assert.state(FileUtils.createDirectory(new File(directory, zipEntry.getName())),
          newSystemException("Failed to create directory [%s] for ZIP entry", zipEntry.getName()));
      }
      else {

        DataInputStream entryInputStream = null;

        DataOutputStream entryOutputStream = null;

        try {

          File zipEntryFile = new File(directory, zipEntry.getName());

          Assert.state(FileUtils.createDirectory(zipEntryFile.getParentFile()),
            newSystemException("Failed to create directory [%1$s] for entry [%2$s]",
              zipEntryFile.getParent(), zipEntry.getName()));

          Assert.state(zipEntryFile.createNewFile(),
            newSystemException("Filed to create file [%1$s] for entry [%2$s]", zipEntryFile, zipEntry.getName()));

          entryInputStream = new DataInputStream(zipFile.getInputStream(zipEntry));
          entryOutputStream = new DataOutputStream(new FileOutputStream(zipEntryFile));

          IOUtils.copy(entryInputStream, entryOutputStream);

        }
        catch (IOException cause) {
          throw newSystemException(cause, "Failed to unzip entry [%s]", zipEntry.getName());
        }
        finally {
          IOUtils.close(entryInputStream);
          IOUtils.close(entryOutputStream);
        }
      }
    });
  }

  /**
   * Zips the contents of the specified {@link File directory}.
   *
   * @param directory {@link File} referring to the file system path/location containing the contents to zip.
   * @return a {@link File ZIP file} containing the compressed contents of the specified {@link File directory}.
   * @throws IllegalArgumentException if {@link File directory} is not a valid directory.
   * @throws IllegalStateException if the {@link File ZIP file } could not be created/initialized.
   * @throws IOException if the contents of the specified {@link File directory} could not be zipped.
   * @throws SystemException if a file system entry could not be added to the {@link File ZIP file}.
   * @see #zip(File, File, ZipOutputStream)
   * @see java.io.File
   */
  public static File zip(File directory) throws IOException {

    Assert.isTrue(FileUtils.isDirectory(directory), "[%s] is not a valid directory", directory);

    File zip = new File(directory.getParent(), directory.getName().concat(ZIP_FILE_EXTENSION));

    Assert.state(zip.createNewFile(), "Failed to create new ZIP file [%s]", zip);

    try (ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(zip, false))) {
      zip(directory, directory, outputStream);
      outputStream.finish();
    }

    return zip;
  }

  /**
   * Zips the contents of the specified {@link File directory} to the supplied {@link ZipOutputStream}.
   *
   * @param zipDirectory {@link File directory} being zipped.
   * @param relativeDirectory the current {@link File directory} with contents to zip.
   * @param outputStream {@link ZipOutputStream} used to zip the contents of the relative {@link File directory}.
   * @throws IllegalArgumentException if relative {@link File directory} is not a valid directory.
   * @throws SystemException if a file system entry could not be added to the {@link File ZIP file}.
   * @see #zipEntry(File, File, ZipOutputStream)
   * @see java.util.zip.ZipOutputStream
   * @see java.io.File
   */
  @SuppressWarnings("all")
  private static void zip(File zipDirectory, File relativeDirectory, ZipOutputStream outputStream) {

    Assert.isTrue(FileUtils.isDirectory(relativeDirectory),
      "[%s] is not a valid directory", relativeDirectory);

    stream(relativeDirectory.listFiles()).forEach(file -> {

      if (file.isDirectory()) {
        zip(zipDirectory, file, outputStream);
      }
      else {
        zipEntry(zipDirectory, file, outputStream);
      }
    });
  }

  /**
   * Zips the contents of the individual {@link File file system path} to the supplied {@link ZipOutputStream}.
   *
   * @param zipDirectory {@link File directory} being zipped.
   * @param path {@link File} to zip and add to the supplied {@link ZipOutputStream}.
   * @param outputStream {@link ZipOutputStream} used to zip the contents of the given {@link File path}.
   * @return the given {@link ZipOutputStream}.
   * @throws SystemException if {@link File path} could not be zipped and added to the supplied {@link ZipOutputStream}.
   * @see #zipEntry(ZipEntry, ZipOutputStream)
   * @see java.util.zip.ZipOutputStream
   * @see java.io.File
   */
  static ZipOutputStream zipEntry(File zipDirectory, File path, ZipOutputStream outputStream) {
    return zipEntry(newZipEntry(zipDirectory, path), outputStream);
  }

  /**
   * Zips the contents of the individual {@link ZipEntry} to the supplied {@link ZipOutputStream}.
   *
   * @param entry {@link ZipEntry} to compress and add to the supplied {@link ZipOutputStream}.
   * @param outputStream {@link ZipOutputStream} used to zip the contents of the given {@link ZipEntry}.
   * @return the given {@link ZipOutputStream}.
   * @throws SystemException if {@link ZipEntry} could not be compressed and added to
   * the supplied {@link ZipOutputStream}.
   * @see java.util.zip.ZipOutputStream
   * @see java.util.zip.ZipEntry
   */
  static ZipOutputStream zipEntry(ZipEntry entry, ZipOutputStream outputStream) {

    try {
      outputStream.putNextEntry(entry);
    }
    catch (IOException cause) {
      throw newSystemException(cause,"Failed to zip entry [%s]", entry.getName());
    }

    IOUtils.doSafeIo(outputStream::closeEntry);

    return outputStream;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy