001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.commons.io; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 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.GZIPInputStream; 031import java.util.zip.GZIPOutputStream; 032import java.util.zip.ZipEntry; 033import java.util.zip.ZipFile; 034import java.util.zip.ZipInputStream; 035import java.util.zip.ZipOutputStream; 036 037import lucee.aprint; 038import lucee.commons.io.res.Resource; 039import lucee.commons.io.res.ResourceProvider; 040import lucee.commons.io.res.ResourcesImpl; 041import lucee.commons.io.res.filter.ExtensionResourceFilter; 042import lucee.commons.io.res.filter.OrResourceFilter; 043import lucee.commons.io.res.filter.ResourceFilter; 044import lucee.commons.lang.StringUtil; 045import lucee.runtime.op.Caster; 046 047import org.apache.commons.compress.archivers.tar.TarArchiveEntry; 048import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; 049import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; 050import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; 051 052/** 053 * Util to manipulate zip files 054 */ 055public final class CompressUtil { 056 057 /** 058 * Field <code>FORMAT_ZIP</code> 059 */ 060 public static final int FORMAT_ZIP=0; 061 /** 062 * Field <code>FORMAT_TAR</code> 063 */ 064 public static final int FORMAT_TAR=1; 065 /** 066 * Field <code>FORMAT_TGZ</code> 067 */ 068 public static final int FORMAT_TGZ=2; 069 /** 070 * Field <code>FORMAT_GZIP</code> 071 */ 072 public static final int FORMAT_GZIP=3; 073 /** 074 * Field <code>FORMAT_BZIP</code> 075 */ 076 public static final int FORMAT_BZIP=4; 077 /** 078 * Field <code>FORMAT_BZIP</code> 079 */ 080 public static final int FORMAT_BZIP2=4; 081 082 /** 083 * Field <code>FORMAT_TBZ</code> 084 */ 085 public static final int FORMAT_TBZ=5; 086 /** 087 * Field <code>FORMAT_TBZ2</code> 088 */ 089 public static final int FORMAT_TBZ2=5; 090 091 /** 092 * Constructor of the class 093 */ 094 private CompressUtil(){} 095 096 /** 097 * extract a zip file to a directory 098 * @param format 099 * @param source 100 * @param target 101 * @throws IOException 102 */ 103 public static void extract(int format,Resource source, Resource target) throws IOException { 104 if(format==FORMAT_ZIP) extractZip(source,target); 105 else if(format==FORMAT_TAR) extractTar(source,target); 106 else if(format==FORMAT_GZIP)extractGZip(source,target); 107 else if(format==FORMAT_TGZ) extractTGZ(source,target); 108 else throw new IOException("can't extract in given format"); 109 } 110 111 public static void list(int format,Resource source) throws IOException { 112 if(format==FORMAT_ZIP) listZip(source); 113 //else if(format==FORMAT_TAR) listar(source); 114 //else if(format==FORMAT_GZIP)listGZip(source); 115 //else if(format==FORMAT_TGZ) listTGZ(source); 116 else throw new IOException("can't list in given format, atm only zip files are supported"); 117 } 118 119 120 121 private static void extractTGZ(Resource source, Resource target) throws IOException { 122 //File tmpTarget = File.createTempFile("_temp","tmp"); 123 Resource tmp = SystemUtil.getTempDirectory().getRealResource(System.currentTimeMillis()+".tmp"); 124 try { 125 // read Gzip 126 extractGZip(source, tmp); 127 128 // read Tar 129 extractTar(tmp, target); 130 } 131 finally { 132 tmp.delete(); 133 } 134 } 135 136 private static void extractGZip(Resource source, Resource target) throws IOException { 137 InputStream is=null; 138 OutputStream os=null; 139 try { 140 is = new GZIPInputStream(IOUtil.toBufferedInputStream(source.getInputStream())); 141 os = IOUtil.toBufferedOutputStream(target.getOutputStream()); 142 IOUtil.copy(is,os,false,false); 143 } 144 finally { 145 IOUtil.closeEL(is, os); 146 } 147 } 148 149 /** 150 * extract a zip file to a directory 151 * @param format 152 * @param sources 153 * @param target 154 * @throws IOException 155 */ 156 public static void extract(int format,Resource[] sources, Resource target) throws IOException { 157 if(format==FORMAT_ZIP || format==FORMAT_TAR) { 158 for(int i=0;i<sources.length;i++) { 159 extract(format,sources[i],target); 160 } 161 } 162 else throw new IOException("can't extract in given format"); 163 } 164 165 private static void extractTar(Resource tarFile, Resource targetDir) throws IOException { 166 if(!targetDir.exists() || !targetDir.isDirectory()) 167 throw new IOException(targetDir+" is not a existing directory"); 168 169 if(!tarFile.exists()) 170 throw new IOException(tarFile+" is not a existing file"); 171 172 if(tarFile.isDirectory()) { 173 Resource[] files = tarFile.listResources(new ExtensionResourceFilter("tar")); 174 if(files==null) 175 throw new IOException("directory "+tarFile+" is empty"); 176 extract(FORMAT_TAR,files,targetDir); 177 return; 178 } 179 180// read the zip file and build a query from its contents 181 TarArchiveInputStream tis=null; 182 try { 183 tis = new TarArchiveInputStream( IOUtil.toBufferedInputStream(tarFile.getInputStream()) ) ; 184 TarArchiveEntry entry; 185 int mode; 186 while ( ( entry = tis.getNextTarEntry()) != null ) { 187 //print.ln(entry); 188 Resource target=targetDir.getRealResource(entry.getName()); 189 if(entry.isDirectory()) { 190 target.mkdirs(); 191 } 192 else { 193 Resource parent=target.getParentResource(); 194 if(!parent.exists())parent.mkdirs(); 195 IOUtil.copy(tis,target,false); 196 } 197 target.setLastModified(entry.getModTime().getTime()); 198 mode=entry.getMode(); 199 if(mode>0)target.setMode(mode); 200 //tis.closeEntry() ; 201 } 202 } 203 finally { 204 IOUtil.closeEL(tis); 205 } 206 } 207 208 private static void extractZip(Resource zipFile, Resource targetDir) throws IOException { 209 if(!targetDir.exists() || !targetDir.isDirectory()) 210 throw new IOException(targetDir+" is not a existing directory"); 211 212 if(!zipFile.exists()) 213 throw new IOException(zipFile+" is not a existing file"); 214 215 if(zipFile.isDirectory()) { 216 Resource[] files = zipFile.listResources( 217 new OrResourceFilter(new ResourceFilter[]{ 218 new ExtensionResourceFilter("zip"), 219 new ExtensionResourceFilter("jar"), 220 new ExtensionResourceFilter("war"), 221 new ExtensionResourceFilter("tar"), 222 new ExtensionResourceFilter("ear") 223 }) 224 ); 225 if(files==null) 226 throw new IOException("directory "+zipFile+" is empty"); 227 extract(FORMAT_ZIP,files,targetDir); 228 return; 229 } 230 231// read the zip file and build a query from its contents 232 unzip(zipFile,targetDir); 233 /*ZipInputStream zis=null; 234 try { 235 zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ; 236 ZipEntry entry; 237 while ( ( entry = zis.getNextEntry()) != null ) { 238 Resource target=targetDir.getRealResource(entry.getName()); 239 if(entry.isDirectory()) { 240 target.mkdirs(); 241 } 242 else { 243 Resource parent=target.getParentResource(); 244 if(!parent.exists())parent.mkdirs(); 245 246 IOUtil.copy(zis,target,false); 247 } 248 target.setLastModified(entry.getTime()); 249 zis.closeEntry() ; 250 } 251 } 252 finally { 253 IOUtil.closeEL(zis); 254 }*/ 255 } 256 257 258 259 260 private static void unzip(Resource zipFile,Resource targetDir) throws IOException { 261 /*if(zipFile instanceof File){ 262 unzip((File)zipFile, targetDir); 263 return; 264 }*/ 265 266 ZipInputStream zis=null; 267 try { 268 zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ; 269 ZipEntry entry; 270 while ( ( entry = zis.getNextEntry()) != null ) { 271 Resource target=targetDir.getRealResource(entry.getName()); 272 if(entry.isDirectory()) { 273 target.mkdirs(); 274 } 275 else { 276 Resource parent=target.getParentResource(); 277 if(!parent.exists())parent.mkdirs(); 278 IOUtil.copy(zis,target,false); 279 } 280 target.setLastModified(entry.getTime()); 281 zis.closeEntry() ; 282 } 283 } 284 finally { 285 IOUtil.closeEL(zis); 286 } 287 } 288 289 private static void listZip(Resource zipFile) throws IOException { 290 if(!zipFile.exists()) 291 throw new IOException(zipFile+" is not a existing file"); 292 293 if(zipFile.isDirectory()) { 294 throw new IOException(zipFile+" is a directory"); 295 } 296 297 ZipInputStream zis=null; 298 try { 299 zis = new ZipInputStream( IOUtil.toBufferedInputStream(zipFile.getInputStream()) ) ; 300 ZipEntry entry; 301 while ( ( entry = zis.getNextEntry()) != null ) { 302 if(!entry.isDirectory()){ 303 ByteArrayOutputStream baos=new ByteArrayOutputStream(); 304 IOUtil.copy(zis,baos,false,false); 305 byte[] barr = baos.toByteArray(); 306 aprint.o(entry.getName()+":"+barr.length); 307 } 308 } 309 } 310 finally { 311 IOUtil.closeEL(zis); 312 } 313 } 314 315 private static void unzip2(File zipFile,Resource targetDir) throws IOException { 316 ZipFile zf=null; 317 try { 318 zf = new ZipFile(zipFile); 319 320 ZipEntry entry; 321 Enumeration en = zf.entries(); 322 while(en.hasMoreElements()){ 323 entry = (ZipEntry) en.nextElement(); 324 Resource target=targetDir.getRealResource(entry.getName()); 325 if(entry.isDirectory()) { 326 target.mkdirs(); 327 } 328 else { 329 Resource parent=target.getParentResource(); 330 if(!parent.exists())parent.mkdirs(); 331 InputStream is = zf.getInputStream(entry); 332 IOUtil.copy(is,target,true); 333 } 334 target.setLastModified(entry.getTime()); 335 } 336 } 337 finally { 338 IOUtil.closeEL(zf); 339 } 340 } 341 342 /** 343 * compress data to a zip file 344 * @param format format it that should by compressed usally is CompressUtil.FORMAT_XYZ 345 * @param source 346 * @param target 347 * @param includeBaseFolder 348 * @param mode 349 * @throws IOException 350 */ 351 public static void compress(int format, Resource source, Resource target, boolean includeBaseFolder, int mode) throws IOException { 352 if( format==FORMAT_GZIP) compressGZip(source,target); 353 else if(format==FORMAT_BZIP2) compressBZip2(source,target); 354 else { 355 Resource[] sources=(!includeBaseFolder && source.isDirectory())?source.listResources():new Resource[]{source}; 356 compress(format, sources, target,mode); 357 } 358 } 359 360 /** 361 * compress data to a zip file 362 * @param format format it that should by compressed usally is CompressUtil.FORMAT_XYZ 363 * @param sources 364 * @param target 365 * @param mode 366 * @throws IOException 367 */ 368 public static void compress(int format, Resource[] sources, Resource target, int mode) throws IOException { 369 370 if( format==FORMAT_ZIP) compressZip(sources,target,null); 371 else if(format==FORMAT_TAR) compressTar(sources,target,mode); 372 else if(format==FORMAT_TGZ) compressTGZ(sources,target,mode); 373 else if(format==FORMAT_TBZ2) compressTBZ2(sources,target,mode); 374 375 else throw new IOException("can't compress in given format"); 376 } 377 378 /** 379 * compress a source file/directory to a tar/gzip file 380 * @param sources 381 * @param target 382 * @param mode 383 * @throws IOException 384 */ 385 public static void compressTGZ(Resource[] sources, Resource target,int mode) throws IOException { 386 File tmpTarget = File.createTempFile("_temp","tmp"); 387 try { 388 // write Tar 389 OutputStream tmpOs=new FileOutputStream(tmpTarget); 390 try { 391 compressTar(sources,tmpOs,mode); 392 } 393 finally { 394 IOUtil.closeEL(tmpOs); 395 } 396 397 // write Gzip 398 InputStream is = null; 399 OutputStream os = null; 400 try { 401 is = new FileInputStream(tmpTarget); 402 os = target.getOutputStream(); 403 compressGZip(is, os); 404 } 405 finally { 406 IOUtil.closeEL(is,os); 407 } 408 } 409 finally { 410 tmpTarget.delete(); 411 } 412 } 413 414 /** 415 * compress a source file/directory to a tar/bzip2 file 416 * @param sources 417 * @param target 418 * @param mode 419 * @throws IOException 420 */ 421 private static void compressTBZ2(Resource[] sources, Resource target, int mode) throws IOException { 422 //File tmpTarget = File.createTempFile("_temp","tmp"); 423 ByteArrayOutputStream baos=new ByteArrayOutputStream(); 424 compressTar(sources,baos,mode); 425 _compressBZip2(new ByteArrayInputStream(baos.toByteArray()), target.getOutputStream()); 426 //tmpTarget.delete(); 427 } 428 429 /** 430 * compress a source file to a gzip file 431 * @param source 432 * @param target 433 * @throws IOException 434 * @throws IOException 435 */ 436 private static void compressGZip(Resource source, Resource target) throws IOException { 437 if(source.isDirectory()) { 438 throw new IOException("you can only create a GZIP File from a single source file, use TGZ (TAR-GZIP) to first TAR multiple files"); 439 } 440 InputStream is=null; 441 OutputStream os=null; 442 try { 443 is = source.getInputStream(); 444 os = target.getOutputStream(); 445 } catch(IOException ioe) { 446 IOUtil.closeEL(is, os); 447 throw ioe; 448 } 449 compressGZip(is,os); 450 451 } 452 453 public static void compressGZip(InputStream source, OutputStream target) throws IOException { 454 InputStream is = IOUtil.toBufferedInputStream(source); 455 if(!(target instanceof GZIPOutputStream)) target = new GZIPOutputStream(IOUtil.toBufferedOutputStream(target)); 456 IOUtil.copy(is,target,true,true); 457 } 458 459 /** 460 * compress a source file to a bzip2 file 461 * @param source 462 * @param target 463 * @throws IOException 464 */ 465 private static void compressBZip2(Resource source, Resource target) throws IOException { 466 if(source.isDirectory()) { 467 throw new IOException("you can only create a BZIP File from a single source file, use TBZ (TAR-BZIP2) to first TAR multiple files"); 468 } 469 InputStream is=null; 470 OutputStream os=null; 471 try { 472 is=source.getInputStream(); 473 os=target.getOutputStream(); 474 } 475 catch(IOException ioe) { 476 IOUtil.closeEL(is, os); 477 throw ioe; 478 } 479 480 _compressBZip2(is, os); 481 } 482 483 /** 484 * compress a source file to a bzip2 file 485 * @param source 486 * @param target 487 * @throws IOException 488 */ 489 private static void _compressBZip2(InputStream source, OutputStream target) throws IOException { 490 491 InputStream is = IOUtil.toBufferedInputStream(source); 492 OutputStream os = new BZip2CompressorOutputStream(IOUtil.toBufferedOutputStream(target)); 493 IOUtil.copy(is,os,true,true); 494 } 495 496 /** 497 * compress a source file/directory to a zip file 498 * @param sources 499 * @param target 500 * @param filter 501 * @throws IOException 502 */ 503 public static void compressZip(Resource[] sources, Resource target, ResourceFilter filter) throws IOException { 504 ZipOutputStream zos = null; 505 try { 506 zos = new ZipOutputStream(IOUtil.toBufferedOutputStream(target.getOutputStream())); 507 compressZip("",sources, zos, filter); 508 } 509 finally { 510 IOUtil.closeEL(zos); 511 } 512 } 513 514 public static void compressZip( Resource[] sources, ZipOutputStream zos, ResourceFilter filter) throws IOException { 515 compressZip("",sources, zos, filter); 516 } 517 518 519 private static void compressZip(String parent, Resource[] sources, ZipOutputStream zos, ResourceFilter filter) throws IOException { 520 if(parent.length()>0)parent+="/"; 521 for(int i=0;i<sources.length;i++) { 522 compressZip(parent+sources[i].getName(),sources[i],zos,filter); 523 } 524 } 525 526 private static void compressZip(String parent, Resource source, ZipOutputStream zos, ResourceFilter filter) throws IOException { 527 if(source.isFile()) { 528 //if(filter.accept(source)) { 529 ZipEntry ze=new ZipEntry(parent); 530 ze.setTime(source.lastModified()); 531 zos.putNextEntry(ze); 532 try { 533 IOUtil.copy(source,zos,false); 534 } 535 finally { 536 zos.closeEntry(); 537 } 538 //} 539 } 540 else if(source.isDirectory()) { 541 if(!StringUtil.isEmpty(parent)) { 542 ZipEntry ze=new ZipEntry(parent+"/"); 543 ze.setTime(source.lastModified()); 544 try { 545 zos.putNextEntry(ze); 546 } 547 catch(IOException ioe) { 548 if(Caster.toString(ioe.getMessage()).indexOf("duplicate")==-1)throw ioe; 549 } 550 zos.closeEntry(); 551 } 552 compressZip(parent, filter==null?source.listResources():source.listResources(filter),zos,filter); 553 } 554 } 555 556 /** 557 * compress a source file/directory to a tar file 558 * @param sources 559 * @param target 560 * @param mode 561 * @throws IOException 562 */ 563 public static void compressTar(Resource[] sources,Resource target, int mode) throws IOException { 564 compressTar(sources, IOUtil.toBufferedOutputStream(target.getOutputStream()), mode); 565 } 566 567 public static void compressTar(Resource[] sources,OutputStream target, int mode) throws IOException { 568 if(target instanceof TarArchiveOutputStream){ 569 compressTar("",sources,(TarArchiveOutputStream)target,mode); 570 return; 571 } 572 TarArchiveOutputStream tos=new TarArchiveOutputStream(target); 573 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU); 574 try { 575 compressTar("",sources, tos,mode); 576 } 577 finally { 578 IOUtil.closeEL(tos); 579 } 580 } 581 582 public static void compressTar(String parent, Resource[] sources, TarArchiveOutputStream tos, int mode) throws IOException { 583 584 if(parent.length()>0)parent+="/"; 585 for(int i=0;i<sources.length;i++) { 586 compressTar(parent+sources[i].getName(),sources[i],tos,mode); 587 } 588 } 589 590 private static void compressTar(String parent, Resource source,TarArchiveOutputStream tos, int mode) throws IOException { 591 if(source.isFile()) { 592 //TarEntry entry = (source instanceof FileResource)?new TarEntry((FileResource)source):new TarEntry(parent); 593 TarArchiveEntry entry = new TarArchiveEntry(parent); 594 595 entry.setName(parent); 596 597 // mode 598 //100777 TODO ist das so ok? 599 if(mode>0) entry.setMode(mode); 600 else if((mode=source.getMode())>0) entry.setMode(mode); 601 602 entry.setSize(source.length()); 603 entry.setModTime(source.lastModified()); 604 tos.putArchiveEntry(entry); 605 try { 606 IOUtil.copy(source,tos,false); 607 } 608 finally { 609 tos.closeArchiveEntry(); 610 } 611 } 612 else if(source.isDirectory()) { 613 compressTar(parent, source.listResources(),tos,mode); 614 } 615 } 616 617 public static void main(String[] args) throws IOException { 618 ResourceProvider frp = ResourcesImpl.getFileResourceProvider(); 619 Resource src = frp.getResource("/Users/mic/temp/a"); 620 621 Resource tgz = frp.getResource("/Users/mic/temp/b/a.tgz"); 622 tgz.getParentResource().mkdirs(); 623 Resource tar = frp.getResource("/Users/mic/temp/b/a.tar"); 624 tar.getParentResource().mkdirs(); 625 Resource zip = frp.getResource("/Users/mic/temp/b/a.zip"); 626 zip.getParentResource().mkdirs(); 627 628 Resource tgz1 = frp.getResource("/Users/mic/temp/b/tgz"); 629 tgz1.mkdirs(); 630 Resource tar1 = frp.getResource("/Users/mic/temp/b/tar"); 631 tar1.mkdirs(); 632 Resource zip1 = frp.getResource("/Users/mic/temp/b/zip"); 633 zip1.mkdirs(); 634 635 compressTGZ(new Resource[]{src}, tgz, -1); 636 compressTar(new Resource[]{src}, tar, -1); 637 compressZip(new Resource[]{src}, zip, null); 638 639 extractTGZ(tgz, tgz1); 640 extractTar(tar, tar1); 641 extractZip(src, zip1); 642 643 } 644}