001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 */ 019package org.nuxeo.common.utils; 020 021import java.io.BufferedInputStream; 022import java.io.BufferedOutputStream; 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.net.URL; 030import java.util.ArrayList; 031import java.util.Enumeration; 032import java.util.List; 033import java.util.function.Function; 034import java.util.function.Predicate; 035import java.util.zip.ZipEntry; 036import java.util.zip.ZipFile; 037import java.util.zip.ZipInputStream; 038import java.util.zip.ZipOutputStream; 039 040import org.apache.commons.io.Charsets; 041import org.apache.commons.io.FileUtils; 042import org.apache.commons.io.IOUtils; 043 044/** 045 * @author bstefanescu 046 */ 047public final class ZipUtils { 048 049 // This is an utility class 050 private ZipUtils() { 051 } 052 053 // _____________________________ ZIP ________________________________ 054 055 public static void _putDirectoryEntry(String entryName, ZipOutputStream out) throws IOException { 056 ZipEntry zentry = new ZipEntry(entryName + '/'); 057 out.putNextEntry(zentry); 058 out.closeEntry(); 059 } 060 061 public static void _putFileEntry(File file, String entryName, ZipOutputStream out) throws IOException { 062 try (FileInputStream in = new FileInputStream(file)) { 063 _zip(entryName, in, out); 064 } 065 } 066 067 public static void _zip(String entryName, InputStream in, ZipOutputStream out) throws IOException { 068 ZipEntry zentry = new ZipEntry(entryName); 069 out.putNextEntry(zentry); 070 // Transfer bytes from the input stream to the ZIP file 071 IOUtils.copy(in, out); 072 out.closeEntry(); 073 } 074 075 public static void _zip(String entryName, File file, ZipOutputStream out) throws IOException { 076 // System.out.println("Compressing "+entryName); 077 if (file.isDirectory()) { 078 entryName += '/'; 079 ZipEntry zentry = new ZipEntry(entryName); 080 out.putNextEntry(zentry); 081 out.closeEntry(); 082 File[] files = file.listFiles(); 083 for (int i = 0, len = files.length; i < len; i++) { 084 _zip(entryName + files[i].getName(), files[i], out); 085 } 086 } else { 087 InputStream in = null; 088 try { 089 in = new BufferedInputStream(new FileInputStream(file)); 090 _zip(entryName, in, out); 091 } finally { 092 if (in != null) { 093 in.close(); 094 } 095 } 096 } 097 } 098 099 public static void _zip(File[] files, ZipOutputStream out, String prefix) throws IOException { 100 if (prefix != null) { 101 int len = prefix.length(); 102 if (len == 0) { 103 prefix = null; 104 } else if (prefix.charAt(len - 1) != '/') { 105 prefix += '/'; 106 } 107 } 108 for (int i = 0, len = files.length; i < len; i++) { 109 String name = prefix != null ? prefix + files[i].getName() : files[i].getName(); 110 _zip(name, files[i], out); 111 } 112 } 113 114 public static void zip(File file, OutputStream out, String prefix) throws IOException { 115 if (prefix != null) { 116 int len = prefix.length(); 117 if (len == 0) { 118 prefix = null; 119 } else if (prefix.charAt(len - 1) != '/') { 120 prefix += '/'; 121 } 122 } 123 String name = prefix != null ? prefix + file.getName() : file.getName(); 124 try (ZipOutputStream zout = new ZipOutputStream(out)) { 125 _zip(name, file, zout); 126 } 127 } 128 129 public static void zip(File[] files, OutputStream out, String prefix) throws IOException { 130 try (ZipOutputStream zout = new ZipOutputStream(out)) { 131 _zip(files, zout, prefix); 132 } 133 } 134 135 public static void zip(File file, File zip) throws IOException { 136 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(zip))) { 137 zip(file, out, null); 138 } 139 } 140 141 public static void zip(File[] files, File zip) throws IOException { 142 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(zip))) { 143 zip(files, out, null); 144 } 145 } 146 147 public static void zip(File file, File zip, String prefix) throws IOException { 148 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(zip))) { 149 zip(file, out, prefix); 150 } 151 } 152 153 public static void zip(File[] files, File zip, String prefix) throws IOException { 154 try (OutputStream out = new BufferedOutputStream(new FileOutputStream(zip))) { 155 zip(files, out, prefix); 156 } 157 } 158 159 public static void zipFilesUsingPrefix(String prefix, File[] files, OutputStream out) throws IOException { 160 try (ZipOutputStream zout = new ZipOutputStream(out)) { 161 if (prefix != null && prefix.length() > 0) { 162 int p = prefix.indexOf('/'); 163 while (p > -1) { 164 _putDirectoryEntry(prefix.substring(0, p), zout); 165 p = prefix.indexOf(p + 1, '/'); 166 } 167 _putDirectoryEntry(prefix, zout); 168 prefix += '/'; 169 } else { 170 prefix = ""; 171 } 172 // prefix = prefix + '/'; 173 for (File file : files) { 174 _putFileEntry(file, prefix + file.getName(), zout); 175 } 176 } 177 } 178 179 // _____________________________ UNZIP ________________________________ 180 181 public static void unzip(String prefix, InputStream zipStream, File dir) throws IOException { 182 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(zipStream))) { 183 unzip(prefix, in, dir); 184 } 185 } 186 187 public static void unzip(InputStream zipStream, File dir) throws IOException { 188 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(zipStream))) { 189 unzip(in, dir); 190 } 191 } 192 193 public static void unzip(String prefix, URL zip, File dir) throws IOException { 194 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(zip.openStream()))) { 195 unzip(prefix, in, dir); 196 } 197 } 198 199 public static void unzip(URL zip, File dir) throws IOException { 200 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(zip.openStream()))) { 201 unzip(in, dir); 202 } 203 } 204 205 public static void unzip(String prefix, File zip, File dir) throws IOException { 206 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { 207 unzip(prefix, in, dir); 208 } 209 } 210 211 public static void unzip(File zip, File dir) throws IOException { 212 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { 213 unzip(in, dir); 214 } 215 } 216 217 public static void unzip(String prefix, ZipInputStream in, File dir) throws IOException { 218 unzip(in, dir, entry -> entry.getName().startsWith(prefix), name -> name.substring(prefix.length())); 219 } 220 221 public static void unzip(ZipInputStream in, File dir) throws IOException { 222 unzip(in, dir, entry -> true, Function.identity()); 223 } 224 225 private static void unzip(ZipInputStream in, File dir, Predicate<ZipEntry> filter, 226 Function<String, String> nameFormatter) throws IOException { 227 dir.mkdirs(); 228 ZipEntry entry; 229 while ((entry = in.getNextEntry()) != null) { 230 if (!entry.getName().contains("..") && filter.test(entry)) { 231 File file = new File(dir, nameFormatter.apply(entry.getName())); 232 if (entry.isDirectory()) { 233 file.mkdirs(); 234 } else { 235 file.getParentFile().mkdirs(); 236 try (FileOutputStream output = FileUtils.openOutputStream(file)) { 237 IOUtils.copy(in, output); 238 } 239 } 240 } 241 } 242 } 243 244 public static void unzipIgnoreDirs(ZipInputStream in, File dir) throws IOException { 245 unzip(in, dir, entry -> !entry.isDirectory(), Function.identity()); 246 } 247 248 public static void unzipIgnoreDirs(InputStream zipStream, File dir) throws IOException { 249 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(zipStream))) { 250 unzipIgnoreDirs(in, dir); 251 } 252 } 253 254 public static void unzip(File zip, File dir, PathFilter filter) throws IOException { 255 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { 256 unzip(in, dir, filter); 257 } 258 } 259 260 public static void unzip(ZipInputStream in, File dir, PathFilter filter) throws IOException { 261 if (filter == null) { 262 unzip(in, dir); 263 } else { 264 unzip(in, dir, toPredicate(filter), Function.identity()); 265 } 266 } 267 268 public static void unzip(String prefix, File zip, File dir, PathFilter filter) throws IOException { 269 try (ZipInputStream in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)))) { 270 unzip(prefix, in, dir, filter); 271 } 272 } 273 274 public static void unzip(String prefix, ZipInputStream in, File dir, PathFilter filter) throws IOException { 275 if (filter == null) { 276 unzip(prefix, in, dir); 277 } else { 278 unzip(in, dir, toPredicate(filter).and(entry -> entry.getName().startsWith(prefix)), 279 name -> name.substring(prefix.length())); 280 } 281 } 282 283 private static Predicate<ZipEntry> toPredicate(PathFilter filter) { 284 return entry -> filter.accept(new Path(entry.getName())); 285 } 286 287 // ________________ Entries ________________ 288 /** 289 * Unzip directly the entry. The returned InputStream has to be closed. 290 * 291 * @return the input stream of the desired entry - has to be closed by the caller, or null if not found 292 * @param file the source file 293 * @param entryName the entry name that has to be extracted 294 */ 295 public static InputStream getEntryContentAsStream(File file, String entryName) throws IOException { 296 InputStream result = null; 297 ZipFile zip = new ZipFile(file); 298 ZipEntry entry = zip.getEntry(entryName); 299 if (entry != null) { 300 result = zip.getInputStream(entry); 301 } 302 return result; 303 } 304 305 /** 306 * Unzip directly the entry. 307 * 308 * @return the String content of the entry with name entryName 309 * @param file the source file 310 * @param entryName the entry name that has to be extracted 311 */ 312 public static String getEntryContentAsString(File file, String entryName) throws IOException { 313 try (InputStream resultStream = getEntryContentAsStream(file, entryName)) { 314 return IOUtils.toString(resultStream, Charsets.UTF_8); 315 } 316 } 317 318 /** 319 * Unzips directly the entry. 320 * 321 * @return The byte array content of the entry with name entryName 322 * @param file the source file 323 * @param entryName the entry name that has to be extracted 324 */ 325 public static byte[] getEntryContentAsBytes(File file, String entryName) throws IOException { 326 try (InputStream resultStream = getEntryContentAsStream(file, entryName)) { 327 return IOUtils.toByteArray(resultStream); 328 } 329 } 330 331 /** 332 * Lists the entries on the zip file. 333 * 334 * @param file The zip file 335 * @return The list of entries 336 */ 337 public static List<String> getEntryNames(File file) throws IOException { 338 List<String> result = new ArrayList<String>(); 339 try (ZipFile zip = new ZipFile(file)) { 340 Enumeration<? extends ZipEntry> entries = zip.entries(); 341 while (entries.hasMoreElements()) { 342 ZipEntry entry = entries.nextElement(); 343 result.add(entry.getName()); 344 } 345 } 346 return result; 347 } 348 349 /** 350 * Checks if a zip file contains a specified entry name. 351 * 352 * @param file the zip file 353 * @param entryName The content to be checked 354 * @return True if the file contains entryName. False otherwise 355 */ 356 public static boolean hasEntry(File file, String entryName) throws IOException { 357 List<String> elements = getEntryNames(file); 358 return elements.contains(entryName); 359 } 360 361 public static InputStream getEntryContentAsStream(InputStream stream, String entryName) throws IOException { 362 ZipInputStream zip = new ZipInputStream(stream); 363 ZipEntry entry = zip.getNextEntry(); 364 while (entry != null) { 365 if (entry.getName().equals(entryName)) { 366 return zip; 367 } 368 entry = zip.getNextEntry(); 369 } 370 return null; 371 } 372 373 public static String getEntryContentAsString(InputStream stream, String searchedEntryName) throws IOException { 374 try (InputStream resultStream = getEntryContentAsStream(stream, searchedEntryName)) { 375 return IOUtils.toString(resultStream, Charsets.UTF_8); 376 } 377 } 378 379 public static byte[] getEntryContentAsBytes(InputStream stream, String searchedEntryName) throws IOException { 380 try (InputStream resultStream = getEntryContentAsStream(stream, searchedEntryName)) { 381 return IOUtils.toByteArray(resultStream); 382 } 383 } 384 385 public static List<String> getEntryNames(InputStream stream) throws IOException { 386 387 List<String> result = new ArrayList<String>(); 388 try (ZipInputStream zip = new ZipInputStream(stream)) { 389 while (zip.available() == 1) { 390 ZipEntry entry = zip.getNextEntry(); 391 if (entry != null) { 392 result.add(entry.getName()); 393 } 394 } 395 } 396 return result; 397 } 398 399 public static boolean hasEntry(InputStream stream, String entryName) throws IOException { 400 List<String> elements = getEntryNames(stream); 401 return elements.contains(entryName); 402 } 403 404 public static InputStream getEntryContentAsStream(URL url, String entryName) throws IOException { 405 return getEntryContentAsStream(url.openStream(), entryName); 406 } 407 408 public static String getEntryContentAsString(URL url, String entryName) throws IOException { 409 try (InputStream resultStream = getEntryContentAsStream(url, entryName)) { 410 return IOUtils.toString(resultStream, Charsets.UTF_8); 411 } 412 } 413 414 public static byte[] getEntryContentAsBytes(URL url, String entryName) throws IOException { 415 try (InputStream resultStream = getEntryContentAsStream(url, entryName)) { 416 return IOUtils.toByteArray(resultStream); 417 } 418 } 419 420 public static List<String> getEntryNames(URL url) throws IOException { 421 return getEntryNames(url.openStream()); 422 } 423 424 public static boolean hasEntry(URL url, String entryName) throws IOException { 425 return hasEntry(url.openStream(), entryName); 426 } 427 428}