001package arcommon; 002 003import java.io.BufferedInputStream; 004import java.io.BufferedOutputStream; 005import java.io.File; 006import java.io.FileInputStream; 007import java.io.FileOutputStream; 008import java.io.IOException; 009import java.nio.charset.Charset; 010import java.util.ArrayList; 011import java.util.Collection; 012import java.util.Iterator; 013import java.util.List; 014import java.util.zip.ZipEntry; 015import java.util.zip.ZipInputStream; 016import java.util.zip.ZipOutputStream; 017 018/** 019 * Util class for zip and unzip file methods. 020 */ 021public class ZipUtil { 022 public static final char ZIP_DIRECTORY_SEPARATOR = '/'; 023 024 public static interface ZipStepInterface { 025 /** 026 * Called at the start of zipping a ZIP file. Only called once per ZIP file. 027 */ 028 public void zippingZipFile(String zipFileName); 029 /** 030 * Converts the file name from absolute file name to ZIP file file name. 031 */ 032 public String convertFileName(String fileName); 033 /** 034 * Called per file added to the ZIP file. 035 */ 036 public void zipFile(ZipEntry zipEntry, String shortFileName, String fullFileName, ZipOutputStream zipOutputStream) throws IOException; 037 /** 038 * Called per directory added to the ZIP file. 039 */ 040 public void zipDirectory(String shortFileName, String fullFileName, ZipOutputStream zipOutputStream); 041 } 042 043 public static class ZipStep implements ZipStepInterface { 044 private String toPath; 045 private boolean echo; 046 047 public ZipStep(String ptoPath, boolean pecho) { 048 toPath = ptoPath; 049 echo = pecho; 050 } 051 052 public void zippingZipFile(String zipFileName) { 053 if (echo) { 054 System.out.println("Creating zip: \"" + zipFileName + "\""); 055 } 056 } 057 058 public String convertFileName(String fileName) { 059 if (!fileName.startsWith(toPath)) { 060 throw new RuntimeException("Internal error: \"" + fileName + "\" \"" + toPath + "\""); 061 } 062 063 return fileName.substring(toPath.length()); 064 } 065 066 public void zipFile(ZipEntry zipEntry, String shortFileName, String fileString, ZipOutputStream zipOutputStream) throws IOException { 067 String fullFileName = fileString.toString(); 068 FileInputStream fileInputStream = new FileInputStream(fullFileName); 069 if (echo) { 070 System.out.println(" Adding: \"" + fullFileName + "\" as \"" + shortFileName + "\""); 071 } 072 073 File file = new File(fullFileName); 074 zipEntry.setTime(file.lastModified()); 075 076 byte[] buffer = new byte[1024]; 077 int size; 078 // Read data to the end of the source file and write it to the zip output stream. 079 while ((size = fileInputStream.read(buffer)) > 0) { 080 zipOutputStream.write(buffer, 0, size); 081 } 082 fileInputStream.close(); 083 } 084 085 public void zipDirectory(String shortFileName, String fullFileName, ZipOutputStream zipOutputStream) { 086 ; 087 } 088 } 089 090 public static boolean zipFiles(String target, String path, boolean echo) { 091 List<String> files = FileUtil.findAllFilesInDirectory(path, true); 092 return zipFiles(target, files, path, echo); 093 } 094 095 public static boolean zipFiles(String target, Collection<String> files, boolean echo) { 096 return zipFiles(target, files, "", echo); 097 } 098 099 public static boolean zipFiles(String target, Collection<String> files, String toPath, boolean echo) { 100 return zipFiles(target, files, new ZipStep(toPath, echo)); 101 } 102 103 public static boolean zipFiles(String target, Collection<String> files, ZipStepInterface zipStep) { 104 try { 105 zipStep.zippingZipFile(target); 106 FileUtil.mkdirs(FileUtil.calculatePath(target)); 107 FileOutputStream fileOutputStream = new FileOutputStream(target); 108 ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); 109 zipFiles(zipOutputStream, files, zipStep); 110 zipOutputStream.closeEntry(); // Finish zip process (Maybe) 111 zipOutputStream.finish(); // Finish zip process (Maybe) 112 fileOutputStream.close(); 113 } catch (IOException e) { 114 throw new RuntimeException(e); 115 } 116 return true; 117 } 118 119 public static void zipFiles(ZipOutputStream zipOutputStream, Collection<String> files, ZipStepInterface zipStep) throws IOException { 120 for (Iterator<String> iterator = files.iterator(); iterator.hasNext();) { 121 String fullFileName = iterator.next(); 122 123 String shortFileName = zipStep.convertFileName(fullFileName); 124 shortFileName = shortFileName.replace(File.separatorChar, ZIP_DIRECTORY_SEPARATOR); 125 if (shortFileName.contains("..\\") || shortFileName.contains("../")) { 126 throw new RuntimeException("shortFileName.contains(\"..\\\") || shortFileName.contains(\"..//\") " + shortFileName); 127 } 128 if (shortFileName.startsWith("\\") || shortFileName.startsWith("/")) { 129 throw new RuntimeException("shortFileName.startsWith(\"\\\\\") || shortFileName.startsWith(\"/\")\") " + shortFileName); 130 } 131 ZipEntry zipEntry = new ZipEntry(shortFileName); 132/* zipEntry.setCompressedSize(1000); // Don't work!!!! 133 zipEntry.setSize(1000); // Don't work!!!! 134 zipEntry.setMethod(ZipEntry.STORED); */ 135 zipOutputStream.putNextEntry(zipEntry); // Put a new ZipEntry in the ZipOutputStream 136 137 if (StringUtil.lastChar(shortFileName) == ZIP_DIRECTORY_SEPARATOR || shortFileName.endsWith(File.separator)) { // HACK 138 zipStep.zipDirectory(shortFileName, fullFileName, zipOutputStream); 139 } else { 140 zipStep.zipFile(zipEntry, shortFileName, fullFileName, zipOutputStream); 141 } 142 143 zipOutputStream.flush(); 144 zipOutputStream.closeEntry(); 145 } 146 zipOutputStream.flush(); 147 } 148 149 public static interface UnzipStepInterface { 150 /** 151 * Called at the start of unzipping a ZIP file. Only called once per ZIP file. 152 */ 153 public void unzipZipFile(String zipFileName); 154 /** 155 * Called per file contained in the ZIP file. 156 */ 157 public void unzipFile(String fileName, ZipEntry zipEntry, ZipInputStream zipInputStream) throws IOException; 158 /** 159 * Called per directory contained in the ZIP file. 160 */ 161 public void unzipDirectory(String fileName, ZipEntry zipEntry) throws IOException; 162 } 163 164 public static ZipInputStream createZipInputStream(String zipFile, String charsetName) { 165 // See https://stackoverflow.com/questions/20013091/java-unzip-error-malformed 166 Charset charset; 167 if (charsetName != null) { 168 charset = Charset.forName(charsetName); 169 } else { 170 charset = null; 171 } 172 try { 173 BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(zipFile)); 174 ZipInputStream zipInputStream; 175 if (charset != null) { 176 zipInputStream = new ZipInputStream(bufferedInputStream, charset); 177 } else { 178 zipInputStream = new ZipInputStream(bufferedInputStream); 179 } 180 return zipInputStream; 181 } catch (IOException e) { 182 throw new RuntimeException(e); 183 } 184 } 185 186 public static class UnzipStep implements UnzipStepInterface { 187 private String toPath; 188 private boolean echo; 189 190 public UnzipStep(String ptoPath, boolean pecho) { 191 if (ptoPath == null) { 192 throw new IllegalArgumentException("toPath is null"); 193 } 194 toPath = ptoPath; 195 echo = pecho; 196 197 if (toPath.length() > 0 && !toPath.endsWith("\\") && !toPath.endsWith("/")) { 198 toPath = toPath + "\\"; 199 } 200 } 201 202 public void unzipZipFile(String zipFileName) { 203 if (echo) { 204 System.out.println("Unzipping zip: \"" + zipFileName + "\" to directory \"" + toPath + "\""); 205 } 206 } 207 208 public void unzipFile(String fileName, ZipEntry zipEntry, ZipInputStream zipInputStream) throws IOException { 209 if (echo) { 210 System.out.println(" unzipping: " + fileName); 211 } 212 String fullFileName = toPath + fileName; 213 File parentFile = new File(fullFileName).getParentFile(); 214 if (parentFile != null) { 215 FileUtil.mkdirs(parentFile); 216 } 217 218 byte[] buffer = new byte[1024]; 219 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(fullFileName), buffer.length); 220 int size; 221 // Read data to the end of the source file and write it to the zip output stream. 222 while ((size = zipInputStream.read(buffer, 0, buffer.length)) > 0) { 223 out.write(buffer, 0, size); 224 } 225 226 out.flush(); 227 out.close(); 228 } 229 230 public void unzipDirectory(String fileName, ZipEntry zipEntry) throws IOException { 231 if (echo) { 232 System.out.println(" unzipping directory: " + zipEntry.getName()); 233 } 234 } 235 } 236 237 public static boolean unzipZipFile(String zipFile, String toPath, boolean echo) { 238 try { 239 unzipZipFile(zipFile, new UnzipStep(toPath, echo)); 240 } catch (IOException e) { 241 throw new RuntimeException(e); 242 } 243 return true; 244 } 245 246 public static void unzipZipFile(String zipFile, UnzipStepInterface unzipStep) throws IOException { 247 unzipZipFile(zipFile, unzipStep, null); 248 } 249 250 public static void unzipZipFile(String zipFile, UnzipStepInterface unzipStep, String charsetName) throws IOException { 251 unzipStep.unzipZipFile(zipFile); 252 ZipInputStream zipInputStream = createZipInputStream(zipFile, charsetName); 253 unzipFiles(zipInputStream, unzipStep); 254 zipInputStream.close(); 255 } 256 257 public static void unzipFiles(ZipInputStream zipInputStream, UnzipStepInterface unzipStep) throws IOException { 258 ZipEntry zipEntry; 259 boolean fileFound = false; 260 while ((zipEntry = zipInputStream.getNextEntry()) != null) { 261 fileFound = true; 262 String fileName = zipEntry.getName().replace(ZIP_DIRECTORY_SEPARATOR, File.separatorChar); 263 if (fileName.contains("..\\") || fileName.contains("../")) { 264 throw new RuntimeException("fileName.contains(\"..\\\") || fileName.contains(\"..//\") " + fileName); 265 } 266 if (fileName.startsWith("\\") || fileName.startsWith("/")) { 267 throw new RuntimeException("fileName.startsWith(\"\\\") || fileName.startsWith(\"/\") " + fileName); 268 } 269 if (StringUtil.lastChar(fileName) == ZIP_DIRECTORY_SEPARATOR || fileName.endsWith(File.separator)) { // HACK 270 unzipStep.unzipDirectory(fileName, zipEntry); 271 } else { 272 unzipStep.unzipFile(fileName, zipEntry, zipInputStream); 273 } 274 } 275 if (!fileFound) { 276 throw new RuntimeException("Zip file was empty or ZipUtil.unzipFiles() was called directly with the zipInputStream parameter from UnzipStepInterface.unzipFile()."); 277/* The following code will produce the error above: 278 public void unzipFile(String fileName, ZipEntry zipEntry, ZipInputStream zipInputStream) throws IOException { 279 ZipUtil.unzipFiles(zipInputStream, this); 280 Fix: 281 public void unzipFile(String fileName, ZipEntry zipEntry, ZipInputStream zipInputStream) throws IOException { 282 ZipUtil.unzipFiles(new ZipInputStream(zipInputStream), this); 283 */ 284 } 285 } 286 287 public static List<String> listFilesInZipFile(String zipFile) { 288 return listFilesInZipFile(zipFile, null); 289 } 290 291 public static List<String> listFilesInZipFile(String zipFile, String charsetName) { 292 try { 293 ZipInputStream zipInputStream = createZipInputStream(zipFile, charsetName); 294 List<String> result = listFilesInZipFile(zipInputStream); 295 zipInputStream.close(); 296 return result; 297 } catch (IOException e) { 298 throw new RuntimeException(e); 299 } 300 } 301 302 public static List<String> listFilesInZipFile(ZipInputStream zipInputStream) { 303 final List<String> result = new ArrayList<String>(); 304 try { 305 unzipFiles(zipInputStream, new UnzipStepInterface() { 306 public void unzipZipFile(String zipFileName) { 307 ; 308 } 309 310 public void unzipFile(String fileName, ZipEntry zipEntry, ZipInputStream zipInputStreamTemp) throws IOException { 311 result.add(fileName); 312 } 313 314 public void unzipDirectory(String fileName, ZipEntry zipEntry) throws IOException { 315 ; 316 } 317 }); 318 } catch (IOException e) { 319 throw new RuntimeException(e); 320 } 321 return result; 322 } 323 324 public static void deleteFileInZipFile(String zipFile, final String deleteFileName) { 325 try { 326 String tempFileName = zipFile + "-temp.zip"; 327 FileOutputStream fileOutputStream = new FileOutputStream(tempFileName); 328 final ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream); 329 unzipZipFile(zipFile, new UnzipStepInterface() { 330 @Override 331 public void unzipZipFile(String zipFileName) { 332 333 } 334 335 @Override 336 public void unzipFile(String fileName, ZipEntry zipEntry, ZipInputStream zipInputStream) throws IOException { 337 if (fileName.equals(deleteFileName)) { 338 System.out.println("Deleting: " + fileName); 339 return ; 340 } 341 342 ZipEntry newZipEntry = new ZipEntry(fileName); 343 zipOutputStream.putNextEntry(newZipEntry); // Put a new ZipEntry in the ZipOutputStream 344 345 newZipEntry.setTime(zipEntry.getTime()); 346 347 byte[] buffer = new byte[1024]; 348 int size; 349 // Read data to the end of the source file and write it to the zip output stream. 350 while ((size = zipInputStream.read(buffer)) > 0) { 351 zipOutputStream.write(buffer, 0, size); 352 } 353 } 354 355 @Override 356 public void unzipDirectory(String fileName, ZipEntry zipEntry) throws IOException { 357 358 } 359 }); 360 361 zipOutputStream.closeEntry(); // Finish zip process (Maybe) 362 zipOutputStream.finish(); // Finish zip process (Maybe) 363 fileOutputStream.close(); 364 365 FileUtil.delete(zipFile); 366 FileUtil.rename(tempFileName, zipFile); 367 } catch (IOException e) { 368 throw new RuntimeException(e); 369 } 370 } 371}