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

org.nuiton.util.ZipUtil Maven / Gradle / Ivy

There is a newer version: 3.1
Show newest version
/*
 * #%L
 * Nuiton Utils
 * 
 * $Id: ZipUtil.java 2464 2013-01-13 11:16:02Z tchemit $
 * $HeadURL: http://svn.nuiton.org/svn/nuiton-utils/tags/nuiton-utils-2.6.10/nuiton-utils/src/main/java/org/nuiton/util/ZipUtil.java $
 * %%
 * Copyright (C) 2004 - 2010 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

package org.nuiton.util;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * Opérations sur des fichiers Zip. Compression et décompression avec ou
 * sans filtres, scan des fichiers créés ou écrasés lors de la décompression...
 * 

* Created: 24 août 2006 10:13:35 * * @author bpoussin * $Id: ZipUtil.java 2464 2013-01-13 11:16:02Z tchemit $ */ public class ZipUtil { /** Class logger. */ private static Log log = LogFactory.getLog(ZipUtil.class); /** Taille du buffer pour les lectures/écritures. */ private static final int BUFFER_SIZE = 8 * 1024; /** Le séparateur de fichier en local. */ private static final String LOCAL_SEP = File.separator; private static final String LOCAL_SEP_PATTERN = "\\".equals(LOCAL_SEP) ? LOCAL_SEP + LOCAL_SEP : LOCAL_SEP; /** Le séparateur zip. */ private static final String ZIP_SEP = "/"; private static final String ZIP_SEP_PATTERN = "/"; /** Accept all file pattern. */ protected static FileFilter ALL_FILE_FILTER = new FileFilter() { public boolean accept(File pathname) { return true; } }; /** * Uncompress zipped file in targetDir. * * @param file the zip source file * @param targetDir the destination directory * @return return last entry name * @throws IOException if any problem while uncompressing */ public static String uncompress(File file, File targetDir) throws IOException { String result; result = uncompressAndRename(file, targetDir, null, null); return result; } /** * Uncompress zipped stream in targetDir. * * * @param stream the zip source stream, stream is closed before return * @param targetDir the destination directory * @return return last entry name * @throws IOException if any problem while uncompressing * @since 2.6.6 */ public static String uncompress(InputStream stream, File targetDir) throws IOException { String result = uncompressAndRename(stream, targetDir, null, null); return result; } /** * Uncompress zipped file in targetDir, and rename uncompressed file if * necessary. If renameFrom or renameTo is null no renaming is done *

* file in zip use / to separate directory and not begin with / * each directory ended with / * * @param file the zip source file * @param targetDir the destination directory * @param renameFrom pattern to permit rename file before uncompress it * @param renameTo new name for file if renameFrom is applicable to it * you can use $1, $2, ... if you have '(' ')' in renameFrom * @return return last entry name * @throws IOException if any problem while uncompressing */ public static String uncompressAndRename(File file, File targetDir, String renameFrom, String renameTo) throws IOException { return uncompressAndRename(new FileInputStream(file), targetDir, renameFrom, renameTo); } /** * Uncompress zipped stream in targetDir, and rename uncompressed file if * necessary. If renameFrom or renameTo is null no renaming is done *

* file in zip use / to separate directory and not begin with / * each directory ended with / * * @param stream the zip source stream, stream is closed before return * @param targetDir the destination directory * @param renameFrom pattern to permit rename file before uncompress it * @param renameTo new name for file if renameFrom is applicable to it * you can use $1, $2, ... if you have '(' ')' in renameFrom * @return return last entry name * @throws IOException if any problem while uncompressing * @since 2.6.6 */ public static String uncompressAndRename(InputStream stream, File targetDir, String renameFrom, String renameTo) throws IOException { String result = ""; ZipInputStream in = new ZipInputStream(new BufferedInputStream(stream)); try { ZipEntry entry; while ((entry = in.getNextEntry()) != null) { String name = entry.getName(); if (renameFrom != null && renameTo != null) { name = name.replaceAll(renameFrom, renameTo); if (log.isDebugEnabled()) { log.debug("rename " + entry.getName() + " -> " + name); } } result = name; File target = new File(targetDir, name); if (entry.isDirectory()) { FileUtil.createDirectoryIfNecessary(target); } else { FileUtil.createDirectoryIfNecessary(target.getParentFile()); OutputStream out = new BufferedOutputStream(new FileOutputStream(target)); try { byte[] buffer = new byte[BUFFER_SIZE]; int len; while ((len = in.read(buffer, 0, BUFFER_SIZE)) != -1) { out.write(buffer, 0, len); } } finally { out.close(); } } } } finally { in.close(); } return result; } /** * Compress 'includes' files in zipFile. If file in includes is directory * only the directory is put in zipFile, not the file contained in directory * * @param zipFile the destination zip file * @param root for all file in includes that is in this directory, then we * remove this directory in zip entry name (aka -C for tar), can be null; * @param includes the files to include in zip * @throws IOException if any problem while compressing */ public static void compressFiles(File zipFile, File root, Collection includes) throws IOException { compressFiles(zipFile, root, includes, false); } /** * Compress 'includes' files in zipFile. If file in includes is directory * only the directory is put in zipFile, not the file contained in directory * * @param zipFile the destination zip file * @param root for all file in includes that is in this directory, then we * remove this directory in zip entry name (aka -C for tar), can be null; * @param includes the files to include in zip * @param createMD5 also create a MD5 file (zip name + .md5). MD5 file is created after zip. * @throws IOException if any problem while compressing */ public static void compressFiles(File zipFile, File root, Collection includes, boolean createMD5) throws IOException { OutputStream oStream = new FileOutputStream(zipFile); // if md5 creation flag if (createMD5) { oStream = new MD5OutputStream(oStream); } try { ZipOutputStream zipOStream = new ZipOutputStream(oStream); for (File file : includes) { String entryName = toZipEntryName(root, file); // Création d'une nouvelle entrée dans le zip ZipEntry entry = new ZipEntry(entryName); entry.setTime(file.lastModified()); zipOStream.putNextEntry(entry); if (file.isFile() && file.canRead()) { byte[] readBuffer = new byte[BUFFER_SIZE]; int bytesIn; BufferedInputStream bis = new BufferedInputStream( new FileInputStream(file), BUFFER_SIZE); try { while ((bytesIn = bis.read(readBuffer, 0, BUFFER_SIZE)) != -1) { zipOStream.write(readBuffer, 0, bytesIn); } } finally { bis.close(); } } zipOStream.closeEntry(); } zipOStream.close(); // if md5 creation flag if (createMD5) { String md5hash = StringUtil.asHex(((MD5OutputStream) oStream).hash()); File md5File = new File(zipFile.getAbsoluteFile() + ".md5"); FileUtils.write(md5File, md5hash); } } finally { oStream.close(); } } /** * If fileOrDirectory is directory Compress recursively all file in this * directory, else if is just file compress one file. *

* Entry result name in zip start at fileOrDirectory. * example: if we compress /etc/apache, entry will be apache/http.conf, ... * * @param zipFile the target zip file * @param fileOrDirectory the file or directory to compress * @throws IOException if any problem while compressing */ public static void compress(File zipFile, File fileOrDirectory) throws IOException { compress(zipFile, fileOrDirectory, null, false); } /** * If fileOrDirectory is directory Compress recursively all file in this * directory, else if is just file compress one file. *

* Entry result name in zip start at fileOrDirectory. * example: if we compress /etc/apache, entry will be apache/http.conf, ... * * @param zipFile the target zip file * @param fileOrDirectory the file or directory to compress * @param filter used to accept file, if null, all file is accepted * @throws IOException if any problem while compressing */ public static void compress(File zipFile, File fileOrDirectory, FileFilter filter) throws IOException { compress(zipFile, fileOrDirectory, filter, false); } /** * If fileOrDirectory is directory Compress recursively all file in this * directory, else if is just file compress one file. *

* Entry result name in zip start at fileOrDirectory. * example: if we compress /etc/apache, entry will be apache/http.conf, ... * * @param zipFile the target zip file * @param fileOrDirectory the file or directory to compress * @param filter used to accept file, if null, all file is accepted * @param createMD5 also create a MD5 file (zip name + .md5). MD5 file is created after zip. * @throws IOException if any problem while compressing */ public static void compress(File zipFile, File fileOrDirectory, FileFilter filter, boolean createMD5) throws IOException { if (filter == null) { filter = ALL_FILE_FILTER; } List files = new ArrayList(); if (fileOrDirectory.isDirectory()) { files = FileUtil.getFilteredElements(fileOrDirectory, filter, true); } else if (filter.accept(fileOrDirectory)) { files.add(fileOrDirectory); } compressFiles(zipFile, fileOrDirectory.getParentFile(), files, createMD5); } /** *

  • supprime le root du fichier *
  • Converti les '\' en '/' car les zip entry utilise des '/' *
  • ajoute un '/' a la fin pour les repertoires *
  • supprime le premier '/' si la chaine commence par un '/' * * @param root the root directory * @param file the file to treate * @return the zip entry name corresponding to the given file * from root dir. */ private static String toZipEntryName(File root, File file) { String result = file.getPath(); if (root != null) { String rootPath = root.getPath(); if (result.startsWith(rootPath)) { result = result.substring(rootPath.length()); } } result = result.replace('\\', '/'); if (file.isDirectory()) { result += '/'; } while (result.startsWith("/")) { result = result.substring(1); } return result; } /** * Scan a zipFile, and fill two lists of relative paths corresponding of * zip entries. * First list contains all entries to be added while a uncompress operation * on the destination directory targetDir. * Second list contains all entries to be overwritten while a uncompress * operation on the destination directory targetDir. *
    * If targetDir is null we don't fill * existingFiles list. * * @param zipFile location of the zip to scanZip * @param targetDir location of destination for a uncompress operation. * If null we don't test to * find overwritten files. * @param newFiles list of files to be added while a uncompress * @param existingFiles list of files to be overwritten while a uncompress * if the targetDir, * (only use if targetDir is not * null) * @param excludeFilter used to exclude some files * @param renameFrom {@link #uncompressAndRename(File, File, String, String)} * @param renameTo {@link #uncompressAndRename(File, File, String, String)} * @throws IOException if any exception while dealing with zipfile */ public static void scan(File zipFile, File targetDir, List newFiles, List existingFiles, FileFilter excludeFilter, String renameFrom, String renameTo) throws IOException { ZipFile zip = null; try { zip = new ZipFile(zipFile); boolean findExisting = targetDir != null && targetDir.exists(); boolean filter = findExisting && excludeFilter != null; boolean rename = renameFrom != null && renameTo != null; Enumeration entries = zip.entries(); while (entries.hasMoreElements()) { String entryName = entries.nextElement().getName(); if (rename) { entryName = entryName.replaceAll(renameFrom, renameTo); } String name = convertToLocalEntryName(entryName); if (findExisting || filter) { File file = new File(targetDir, name); if (filter && excludeFilter.accept(file)) continue; if (file.exists()) { existingFiles.add(name); continue; } } newFiles.add(name); } } finally { if (zip != null) { zip.close(); } } } @SuppressWarnings({"unchecked"}) public static List[] scanAndExplodeZip(File source, File root, FileFilter excludeFilter) throws IOException { List overwrittenFiles = new ArrayList(); List newFiles = new ArrayList(); // obtain list of relative paths (to add or overwrite) scan(source, root, newFiles, overwrittenFiles, excludeFilter, null, null); return new List[]{newFiles, overwrittenFiles}; } /** * uncompress zipped file in targetDir. *

    * If toTreate if not null nor empty, we use it to filter * entries to uncompress : it contains a list of relative local path of * files to uncompress. * Otherwise just delegate to {@link ZipUtil#uncompress(File, File)}. * * @param file location of zip file * @param targetDir destination directory * @param toTreate list of relative local path of entries to treate * @param renameFrom {@link #uncompressAndRename(File, File, String, String)} * @param renameTo {@link #uncompressAndRename(File, File, String, String)} * @return return last entry name * @throws IOException if nay exception while operation */ public static String uncompress(File file, File targetDir, List toTreate, String renameFrom, String renameTo) throws IOException { if (toTreate == null || toTreate.isEmpty()) { return uncompressAndRename(file, targetDir, renameFrom, renameTo); } boolean rename = renameFrom != null && renameTo != null; String result = ""; ZipEntry entry; ZipInputStream in = new ZipInputStream(new FileInputStream(file)); try { while ((entry = in.getNextEntry()) != null) { String name = entry.getName(); if (rename) { result = convertToLocalEntryName(name.replaceAll(renameFrom, renameTo)); } else { result = convertToLocalEntryName(name); } if (log.isDebugEnabled()) { log.debug("open [" + name + "] : " + result); } if (!toTreate.contains(result)) { continue; } if (log.isDebugEnabled()) { log.debug("copy [" + name + "] : " + result); } File target = new File(targetDir, result); if (entry.isDirectory()) { FileUtil.createDirectoryIfNecessary(target); } else { FileUtil.createDirectoryIfNecessary(target.getParentFile()); OutputStream out = new BufferedOutputStream( new FileOutputStream(target)); try { byte[] buffer = new byte[BUFFER_SIZE]; int len; while ((len = in.read(buffer, 0, BUFFER_SIZE)) != -1) { out.write(buffer, 0, len); } } finally { out.close(); } } } } finally { in.close(); } return result; } /** * Unzip compressed archive and keep non excluded patterns. * * @param file archive file * @param targetDir destination file * @param excludes excludes pattern (pattern must match complete entry name including root folder) * @throws IOException */ public static void uncompressFiltred(File file, File targetDir, String... excludes) throws IOException { ZipFile zipFile = new ZipFile(file); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); // add continue to break loop boolean excludeEntry = false; if (excludes != null) { for (String exclude : excludes) { if (name.matches(exclude)) { excludeEntry = true; } } } if (!excludeEntry) { File target = new File(targetDir, name); if (entry.isDirectory()) { FileUtil.createDirectoryIfNecessary(target); } else { // get inputstream only here FileUtil.createDirectoryIfNecessary(target.getParentFile()); InputStream in = zipFile.getInputStream(entry); try { OutputStream out = new BufferedOutputStream( new FileOutputStream(target)); try { byte[] buffer = new byte[8 * 1024]; int len; while ((len = in.read(buffer, 0, 8 * 1024)) != -1) { out.write(buffer, 0, len); } } finally { out.close(); } } finally { in.close(); } } } } } /** * Tests if the given file is a zip file. * * @param file the file to test * @return {@code true} if the file is a valid zip file, * {@code false} otherwise. * @throws IOException if could not open zip file * @since 2.4.9 */ public static boolean isZipFile(File file) throws IOException { boolean result = false; try { ZipFile zipFile = new ZipFile(file); zipFile.close(); result = true; } catch (IOException e) { // silent test } return result; } protected static String convertToLocalEntryName(String txt) { String s = txt.replaceAll(ZIP_SEP_PATTERN, LOCAL_SEP_PATTERN); if (s.endsWith(ZIP_SEP)) { s = s.substring(0, s.length() - 1); } return s; } }





  • © 2015 - 2025 Weber Informatics LLC | Privacy Policy