001/* 002 * SonarQube 003 * Copyright (C) 2009-2016 SonarSource SA 004 * mailto:contact AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.utils; 021 022import java.io.BufferedInputStream; 023import java.io.File; 024import java.io.FileInputStream; 025import java.io.FileOutputStream; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.OutputStream; 029import java.util.Enumeration; 030import java.util.zip.ZipEntry; 031import java.util.zip.ZipFile; 032import java.util.zip.ZipInputStream; 033import java.util.zip.ZipOutputStream; 034import org.apache.commons.io.FileUtils; 035import org.apache.commons.io.IOUtils; 036 037/** 038 * @since 1.10 039 */ 040public final class ZipUtils { 041 042 private static final String ERROR_CREATING_DIRECTORY = "Error creating directory: "; 043 044 private ZipUtils() { 045 // only static methods 046 } 047 048 /** 049 * Unzip a file into a directory. The directory is created if it does not exist. 050 * 051 * @return the target directory 052 */ 053 public static File unzip(File zip, File toDir) throws IOException { 054 unzip(zip, toDir, TrueZipEntryFilter.INSTANCE); 055 return toDir; 056 } 057 058 public static File unzip(InputStream zip, File toDir) throws IOException { 059 unzip(zip, toDir, TrueZipEntryFilter.INSTANCE); 060 return toDir; 061 } 062 063 private enum TrueZipEntryFilter implements ZipEntryFilter { 064 INSTANCE; 065 @Override 066 public boolean accept(ZipEntry entry) { 067 return true; 068 } 069 } 070 071 public static File unzip(InputStream stream, File toDir, ZipEntryFilter filter) throws IOException { 072 if (!toDir.exists()) { 073 FileUtils.forceMkdir(toDir); 074 } 075 076 ZipInputStream zipStream = new ZipInputStream(stream); 077 try { 078 ZipEntry entry; 079 while ((entry = zipStream.getNextEntry()) != null) { 080 if (filter.accept(entry)) { 081 File to = new File(toDir, entry.getName()); 082 if (entry.isDirectory()) { 083 throwExceptionIfDirectoryIsNotCreatable(to); 084 } else { 085 File parent = to.getParentFile(); 086 throwExceptionIfDirectoryIsNotCreatable(parent); 087 copy(zipStream, to); 088 } 089 } 090 } 091 return toDir; 092 093 } finally { 094 zipStream.close(); 095 } 096 } 097 098 private static void throwExceptionIfDirectoryIsNotCreatable(File to) throws IOException { 099 if (!to.exists() && !to.mkdirs()) { 100 throw new IOException(ERROR_CREATING_DIRECTORY + to); 101 } 102 } 103 104 public static File unzip(File zip, File toDir, ZipEntryFilter filter) throws IOException { 105 if (!toDir.exists()) { 106 FileUtils.forceMkdir(toDir); 107 } 108 109 ZipFile zipFile = new ZipFile(zip); 110 try { 111 Enumeration<? extends ZipEntry> entries = zipFile.entries(); 112 while (entries.hasMoreElements()) { 113 ZipEntry entry = entries.nextElement(); 114 if (filter.accept(entry)) { 115 File to = new File(toDir, entry.getName()); 116 if (entry.isDirectory()) { 117 throwExceptionIfDirectoryIsNotCreatable(to); 118 } else { 119 File parent = to.getParentFile(); 120 throwExceptionIfDirectoryIsNotCreatable(parent); 121 copy(zipFile, entry, to); 122 } 123 } 124 } 125 return toDir; 126 127 } finally { 128 zipFile.close(); 129 } 130 } 131 132 private static void copy(ZipInputStream zipStream, File to) throws IOException { 133 FileOutputStream fos = null; 134 try { 135 fos = new FileOutputStream(to); 136 IOUtils.copy(zipStream, fos); 137 } finally { 138 IOUtils.closeQuietly(fos); 139 } 140 } 141 142 private static void copy(ZipFile zipFile, ZipEntry entry, File to) throws IOException { 143 FileOutputStream fos = new FileOutputStream(to); 144 InputStream input = null; 145 try { 146 input = zipFile.getInputStream(entry); 147 IOUtils.copy(input, fos); 148 } finally { 149 IOUtils.closeQuietly(input); 150 IOUtils.closeQuietly(fos); 151 } 152 } 153 154 public static void zipDir(File dir, File zip) throws IOException { 155 OutputStream out = null; 156 ZipOutputStream zout = null; 157 try { 158 out = FileUtils.openOutputStream(zip); 159 zout = new ZipOutputStream(out); 160 doZipDir(dir, zout); 161 162 } finally { 163 IOUtils.closeQuietly(zout); 164 IOUtils.closeQuietly(out); 165 } 166 } 167 168 private static void doZip(String entryName, InputStream in, ZipOutputStream out) throws IOException { 169 ZipEntry entry = new ZipEntry(entryName); 170 out.putNextEntry(entry); 171 IOUtils.copy(in, out); 172 out.closeEntry(); 173 } 174 175 private static void doZip(String entryName, File file, ZipOutputStream out) throws IOException { 176 if (file.isDirectory()) { 177 entryName += "/"; 178 ZipEntry entry = new ZipEntry(entryName); 179 out.putNextEntry(entry); 180 out.closeEntry(); 181 File[] files = file.listFiles(); 182 // java.io.File#listFiles() returns null if object is a directory (not possible here) or if 183 // an I/O error occurs (weird!) 184 if (files == null) { 185 throw new IllegalStateException("Fail to list files of directory " + file.getAbsolutePath()); 186 } 187 for (File f : files) { 188 doZip(entryName + f.getName(), f, out); 189 } 190 191 } else { 192 InputStream in = null; 193 try { 194 in = new BufferedInputStream(new FileInputStream(file)); 195 doZip(entryName, in, out); 196 } finally { 197 IOUtils.closeQuietly(in); 198 } 199 } 200 } 201 202 private static void doZipDir(File dir, ZipOutputStream out) throws IOException { 203 File[] children = dir.listFiles(); 204 if (children == null) { 205 throw new IllegalStateException("Fail to list files of directory " + dir.getAbsolutePath()); 206 } 207 for (File child : children) { 208 doZip(child.getName(), child, out); 209 } 210 } 211 212 @FunctionalInterface 213 public interface ZipEntryFilter { 214 boolean accept(ZipEntry entry); 215 } 216 217}