001/** 002 * Please feel free to use any fragment of the code in this file that you need in your own 003 * work. As far as I am concerned, it's in the public domain. No permission is necessary 004 * or required. Credit is always appreciated if you use a large chunk or base a 005 * significant product on one of my examples, but that's not required either. 006 * 007 * This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 008 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 009 * PURPOSE. 010 * 011 * --- Joseph A. Huwaldt 012 */ 013package jahuwaldt.io; 014 015import java.io.*; 016import java.nio.ByteBuffer; 017import java.util.ArrayList; 018import java.util.List; 019import static java.util.Objects.requireNonNull; 020import java.util.zip.GZIPInputStream; 021import java.util.zip.GZIPOutputStream; 022 023/** 024 * This is a utility class of static methods for working with files. 025 * 026 * <p> Modified by: Joseph A. Huwaldt </p> 027 * 028 * @author Joseph A. Huwaldt, Date: November 27, 2009 029 * @version July 7, 2018 030 */ 031public final class FileUtils { 032 // This class now requires Java 1.7 or later! 033 034 /** 035 * A list of characters that are illegal on some of the file systems supported by this 036 * program. The characters in this list include: 037 * \n,\r,\t,\0,\f,',?,*,<,>,|,",:,~,@,!,#,[,],=,+,;, and ','. 038 */ 039 private static final char[] ILLEGAL_CHARACTERS 040 = {'\n', '\r', '\t', '\0', '\f', '`', '?', '*', '<', '>', '|', '\"', ':', '~', '@', '!', '#', 041 '[', ']', '=', '+', ';', ','}; 042 043 /** 044 * Prevent instantiation of this utility class. 045 */ 046 private FileUtils() { 047 } 048 049 /** 050 * Replace any potentially illegal characters from a file name with '_'. 051 * 052 * @param name The file name to be cleaned of potentially illegal characters. May not 053 * be null. 054 * @return The input file name with potentially illegal characters replaced with "_". 055 */ 056 public static String cleanFileName(String name) { 057 if (name.length() == 0) 058 return name; 059 for (char c : ILLEGAL_CHARACTERS) { 060 name = name.replace(c, '_'); 061 } 062 return name; 063 } 064 065 /** 066 * Returns true if the supplied file name contains characters that are illegal on some 067 * file systems. 068 * 069 * @param name The file name to be checked for potentially illegal characters. May not 070 * be null. 071 * @return true if the file name contains potentially illegal characters, false if it 072 * is safe. 073 */ 074 public static boolean hasIllegalChars(String name) { 075 if (name.length() == 0) 076 return false; 077 for (char c : ILLEGAL_CHARACTERS) { 078 if (name.indexOf(c) >= 0) 079 return true; 080 } 081 return false; 082 } 083 084 /** 085 * Return the file name of the specified file without the extension. 086 * 087 * @param file The file to have the name without extension returned. May not be null. 088 * @return The name of the specified file without the extension (if there is one). 089 */ 090 public static String getFileNameWithoutExtension(File file) { 091 String name = file.getName(); 092 return getFileNameWithoutExtension(name); 093 } 094 095 /** 096 * Return the file name of the specified file without the extension. 097 * 098 * @param name The file name to have the name without extension returned. May not be 099 * null. 100 * @return The name of the specified file without the extension (if there is one). 101 */ 102 public static String getFileNameWithoutExtension(String name) { 103 int index = name.lastIndexOf('.'); 104 if (index > 0 && index <= name.length() - 2) { 105 name = name.substring(0, index); 106 } 107 return name; 108 } 109 110 /** 111 * Return the extension portion of the file's name. The extension will always be 112 * returned in lower case and without the ".". 113 * 114 * @param file The file for which the extension is to be returned. May not be null. 115 * @return The extension portion of the file's name without the "." or "" if there is 116 * no extension. 117 * @see #getExtension(java.lang.String) 118 */ 119 public static String getExtension(File file) { 120 requireNonNull(file, "file == null"); 121 return getExtension(file.getName()); 122 } 123 124 /** 125 * Return the extension portion of the file's name. The extension will always be 126 * returned in lower case and without the ".". 127 * 128 * @param name The name of the file, including the extension. May not be null. 129 * @return The extension portion of the file's name without the "." or "" if there is 130 * no extension. 131 * @see #getExtension(java.io.File) 132 */ 133 public static String getExtension(String name) { 134 int i = name.lastIndexOf('.'); 135 if (i > 0 && i < name.length() - 1) 136 return name.substring(i + 1).toLowerCase(); 137 return ""; 138 } 139 140 /** 141 * Copy a file from the source to the destination locations. 142 * 143 * @param src The source file. May not be null. 144 * @param dst The destination file to copy the source file to. May not be null. 145 * @throws java.io.IOException if there is any problem reading from or writing to the 146 * files. 147 */ 148 public static void copy(File src, File dst) throws IOException { 149 requireNonNull(src, "src == null"); 150 requireNonNull(dst, "dst == null"); 151 152 // If src and dst are the same; hence no copying is required. 153 if (sameFile(src, dst)) 154 return; 155 156 InputStream in = null; 157 OutputStream out = null; 158 try { 159 160 in = new FileInputStream(src); 161 out = new FileOutputStream(dst); 162 copy(in, out); 163 164 } finally { 165 if (in != null) 166 in.close(); 167 if (out != null) 168 out.close(); 169 } 170 } 171 172 /** 173 * Copy the input stream to the output stream. 174 * 175 * @param in The source input stream. May not be null. 176 * @param out The destination output stream. May not be null. 177 * @throws java.io.IOException if there is any problem reading from or writing to the 178 * streams. 179 */ 180 public static void copy(InputStream in, OutputStream out) throws IOException { 181 requireNonNull(in, "in == null"); 182 requireNonNull(out, "out == null"); 183 184 byte[] buf = new byte[1024]; 185 int len; 186 while ((len = in.read(buf)) > 0) { 187 out.write(buf, 0, len); 188 } 189 } 190 191 /** 192 * Recursively copy the contents of an entire directory tree from source to 193 * destination. This also works if a single file is passed as the source and 194 * destination. 195 * 196 * @param source The source directory or file to copy. May not be null. 197 * @param destination The destination directory or file. May not be null. 198 * @throws java.io.IOException If there is any problem copying the directories or 199 * files. 200 */ 201 public static void copyDirectory(File source, File destination) throws IOException { 202 if (source.isDirectory()) { 203 if (!destination.exists()) { 204 boolean success = destination.mkdirs(); 205 if (!success) 206 throw new IOException("Could not create directory " + destination); 207 } 208 209 String files[] = source.list(); 210 211 for (String file : files) { 212 File srcFile = new File(source, file); 213 File destFile = new File(destination, file); 214 215 copyDirectory(srcFile, destFile); 216 } 217 } else { 218 copy(source, destination); 219 } 220 } 221 222 /** 223 * Attempt to rename a file from the source File location to the destination File 224 * location. If the standard atomic rename fails, this method falls back on copying 225 * the file which is dangerous as it is not atomic. The fall back will not work if the 226 * source or destination is a directory and an exception is thrown in that case. 227 * 228 * @param src The source file to be renamed. Upon exit, this object MAY point to the 229 * same location as "dst" or it may be left unchanged. Either way, barring 230 * an exception, the file will no longer exist at the input src path 231 * location. May not be null. 232 * @param dst The destination path to rename the source file to. May not be null. 233 * @throws java.io.IOException if there is any problem renaming the file. 234 */ 235 public static void rename(File src, File dst) throws IOException { 236 requireNonNull(dst, "dst == null"); 237 238 if (!src.exists()) 239 throw new FileNotFoundException("\"" + src.getPath() + "\" not found."); 240 241 // If the source and destination are the same, no rename is required. 242 if (sameFile(src, dst)) 243 return; 244 245 // Make sure we can write to the destination location. 246 if (dst.exists()) { 247 // The file already exists, can we write to it? 248 if (!dst.canWrite()) 249 throw new IOException("Can not write to \"" + dst.getPath() + "\"."); 250 251 } else { 252 // The file does not already exist. Can we create a file here? 253 boolean success = dst.createNewFile(); 254 if (!success) 255 throw new IOException("Can not write to \"" + dst.getPath() + "\"."); 256 dst.delete(); 257 } 258 259 try { 260 // Try to use the atomic rename first. 261 boolean success = src.renameTo(dst); 262 if (!success) { 263 // Delete the destination file, if it exists. Then wait a second and try again. 264 if (dst.exists()) 265 dst.delete(); 266 Thread.sleep(1000); 267 success = src.renameTo(dst); 268 269 if (!success) { 270 // If renaming the file doesn't work, fall back on copying it. 271 // This doesn't work for directories. 272 if (!src.isFile()) 273 throw new IOException("Move failed, source is dir: \"" + src.getPath() + "\"."); 274 if (dst.isDirectory()) 275 throw new IOException("Move failed, destination is dir: \"" + dst.getPath() + "\"."); 276 copy(src, dst); 277 src.delete(); 278 } 279 } 280 } catch (InterruptedException e) { 281 throw new InterruptedIOException(); 282 } 283 } 284 285 /** 286 * Returns a list of String objects each of which represents a line in the specified 287 * file. The file may optionally be GZIP compressed. 288 * 289 * @param file The possibly GZIP compressed file to be read in. May not be null. 290 * @return A list of String objects, one for each line in the specified file. 291 * @throws java.io.IOException if there is any problem reading from the file. 292 */ 293 public static List<String> readlines(File file) throws IOException { 294 requireNonNull(file, "file == null"); 295 FileInputStream instream = new FileInputStream(file); 296 return readlines(instream); 297 } 298 299 /** 300 * Returns a list of String objects each of which represents a line in the specified 301 * input stream. The input stream may optionally be GZIP compressed. 302 * 303 * @param instream The input stream to be read in. May optionally be GZIP compressed. 304 * May not be null. 305 * @return A list of String objects, one for each line in the specified input stream. 306 * @throws java.io.IOException if there is any problem reading from the stream. 307 */ 308 public static List<String> readlines(InputStream instream) throws IOException { 309 requireNonNull(instream, "instream == null"); 310 311 // Deal with the possibility that the input stream is GZIP compressed. 312 InputStream in = new BufferedInputStream(instream); 313 if (isGZIPCompressed((BufferedInputStream)in)) 314 in = new GZIPInputStream(in); 315 316 List<String> output = new ArrayList(); 317 try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { 318 String aLine; 319 do { 320 aLine = reader.readLine(); 321 if (aLine != null) 322 output.add(aLine); 323 324 } while (aLine != null); 325 326 } 327 328 return output; 329 } 330 331 /** 332 * Write out a list of String objects which represent a line to the specified output 333 * stream. A line ending character is appended to each line. 334 * 335 * @param outFile The path to the output file to be written to. May not be null. 336 * @param lines The list of String objects to be written out. May not be null. 337 * @throws IOException if there is any problem writing to the output file. 338 */ 339 public static void writelines(String outFile, List<String> lines) throws IOException { 340 requireNonNull(outFile, "outFile == null"); 341 requireNonNull(lines, "lines == null"); 342 343 File file = new File(outFile); 344 writelines(file, lines); 345 } 346 347 /** 348 * Write out a list of String objects which represent a line to the specified output 349 * stream. A line ending character is appended to each line. 350 * 351 * @param outFile The output file to be written to. May not be null. 352 * @param lines The list of String objects to be written out. May not be null. 353 * @throws IOException if there is any problem writing to the output file. 354 */ 355 public static void writelines(File outFile, List<String> lines) throws IOException { 356 requireNonNull(outFile, "outFile == null"); 357 requireNonNull(lines, "lines == null"); 358 359 try (FileOutputStream outstream = new FileOutputStream(outFile)) { 360 writelines(outstream, lines); 361 } 362 } 363 364 /** 365 * Write out a list of String objects which represent a line to the specified output 366 * stream. A line ending character is appended to each line. 367 * 368 * @param outstream The output stream to be written to. May not be null. 369 * @param lines The list of String objects to be written out. May not be null. 370 * @throws IOException if there is any problem writing to the output stream. 371 */ 372 public static void writelines(OutputStream outstream, List<String> lines) throws IOException { 373 requireNonNull(outstream, "outstream == null"); 374 requireNonNull(lines, "lines == null"); 375 376 try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outstream))) { 377 378 // Write out the lines to the output file. 379 int size = lines.size(); 380 for (int i = 0; i < size; ++i) { 381 writer.write(lines.get(i)); 382 writer.newLine(); 383 } 384 385 } 386 387 } 388 389 /** 390 * Returns a buffer that contains the contents of the specified file. 391 * 392 * @param file The file to be read into a new ByteBuffer. May not be null. 393 * @return The buffer containing the contents of the specified file. 394 * @throws java.io.IOException if there is any problem reading from the file. 395 */ 396 public static byte[] file2Buffer(File file) throws IOException { 397 398 // Read the file into a byte buffer. 399 if (file.length() > (long)Integer.MAX_VALUE) 400 throw new IOException(file.getName() + " is to large to convert into a ByteBuffer!"); 401 402 int fileSize = (int)file.length(); 403 byte[] buffer = new byte[fileSize]; 404 try (FileInputStream fis = new FileInputStream(file)) { 405 fis.read(buffer, 0, fileSize); 406 } 407 408 return buffer; 409 } 410 411 /** 412 * Write the entire contents of a byte array to the specified file. 413 * 414 * @param buffer The buffer to be written to the file. May not be null. 415 * @param file The file to write the buffer to (it's existing contents will be 416 * overwritten). May not be null. 417 * @throws java.io.IOException if there is any problem writing to the file. 418 */ 419 public static void buffer2File(byte[] buffer, File file) throws IOException { 420 requireNonNull(buffer, "buffer == null"); 421 requireNonNull(file, "file == null"); 422 423 try (FileOutputStream fos = new FileOutputStream(file)) { 424 fos.write(buffer); 425 } 426 427 } 428 429 /** 430 * Returns a ByteBuffer that contains the contents of the specified file. 431 * 432 * @param file The file to be read into a new ByteBuffer. May not be null. 433 * @return The ByteBuffer containing the contents of the specified file. 434 * @throws java.io.IOException if there is any problem reading from the file. 435 */ 436 public static ByteBuffer file2ByteBuffer(File file) throws IOException { 437 438 // Read the file into a byte buffer. 439 if (file.length() > Integer.MAX_VALUE) 440 throw new IOException(file.getName() + " is to large to convert into a ByteBuffer!"); 441 442 int fileSize = (int)file.length(); 443 byte[] mybytearray = new byte[fileSize]; 444 try (FileInputStream fis = new FileInputStream(file)) { 445 fis.read(mybytearray, 0, fileSize); 446 } 447 448 ByteBuffer buffer = ByteBuffer.wrap(mybytearray); 449 450 return buffer; 451 } 452 453 /** 454 * Write the contents of a ByteBuffer, from the beginning up to the current position, 455 * to the specified file. 456 * 457 * @param buffer The buffer to be written to the file. May not be null. 458 * @param file The file to write the buffer to (it's existing contents will be 459 * overwritten). May not be null. 460 * @throws java.io.IOException if there is any problem writing the byte buffer to the 461 * file. 462 */ 463 public static void byteBuffer2File(ByteBuffer buffer, File file) throws IOException { 464 requireNonNull(file); 465 int pos = buffer.position(); 466 buffer.flip(); 467 byte[] mybytearray = new byte[buffer.limit()]; 468 buffer.get(mybytearray); 469 470 try (FileOutputStream fos = new FileOutputStream(file)) { 471 fos.write(mybytearray); 472 } 473 474 buffer.position(pos); 475 } 476 477 /** 478 * Read in an optionally GZIP compressed, delimited text file that contains a regular 479 * array of Double values. The delimiter may be any whitespace. 480 * 481 * @param filePath Path to/Name of the possibly GZIP compressed table file being read 482 * in. 483 * @return A 2D double array of the values in the table file. 484 * @throws IOException if there is any problem reading from the file or parsing the 485 * results. 486 */ 487 public static double[][] loadtxt(String filePath) throws IOException { 488 requireNonNull(filePath, "filePath == null"); 489 return loadtxt(new File(filePath), "\\s+", 0); 490 } 491 492 /** 493 * Read in an optionally GZIP compressed, delimited text file that contains a regular 494 * array of Double values. The delimiter may be any whitespace. 495 * 496 * @param filePath Path to/Name of the possibly GZIP compressed table file being read 497 * in. 498 * @param skiprows Indicates the number of rows to skip at the top of the file (to 499 * skip over a header for instance). 500 * @return A 2D double array of the values in the table file. 501 * @throws IOException if there is any problem reading from the file or parsing the 502 * results. 503 */ 504 public static double[][] loadtxt(String filePath, int skiprows) throws IOException { 505 requireNonNull(filePath, "filePath == null"); 506 return loadtxt(new File(filePath), "\\s+", skiprows); 507 } 508 509 /** 510 * Read in an optionally GZIP compressed, delimited text file that contains a regular 511 * array of Double values. The delimiter may be any whitespace. 512 * 513 * @param filePath Path to/Name of the possibly GZIP compressed table file being read 514 * in. 515 * @param delimiter The delimiter to use to separate columns (such as "," for commas, 516 * or "\\s+" for any whitespace. 517 * @param skiprows Indicates the number of rows to skip at the top of the file (to 518 * skip over a header for instance). 519 * @return A 2D double array of the values in the table file. 520 * @throws IOException if there is any problem reading from the file or parsing the 521 * results. 522 */ 523 public static double[][] loadtxt(String filePath, String delimiter, int skiprows) throws IOException { 524 requireNonNull(filePath, "filePath == null"); 525 requireNonNull(delimiter, "delimiter == null"); 526 return loadtxt(new File(filePath), delimiter, skiprows); 527 } 528 529 /** 530 * Read in an optionally GZIP compressed, delimited text file that contains a regular 531 * array of Double values. The delimiter may be any whitespace. 532 * 533 * @param txtFile The possibly GZIP compressed table file being read in. 534 * @return A 2D double array of the values in the table file. 535 * @throws IOException if there is any problem reading from the file or parsing the 536 * results. 537 */ 538 public static double[][] loadtxt(File txtFile) throws IOException { 539 requireNonNull(txtFile, "txtFile == null"); 540 return loadtxt(txtFile, "\\s+", 0); 541 } 542 543 /** 544 * Read in an optionally GZIP compressed, delimited text file that contains a regular 545 * array of Double values. The delimiter may be any whitespace. 546 * 547 * @param txtFile The possibly GZIP compressed table file being read in. 548 * @param skiprows Indicates the number of rows to skip at the top of the file (to 549 * skip over a header for instance). 550 * @return A 2D double array of the values in the table file. 551 * @throws IOException if there is any problem reading from the file or parsing the 552 * results. 553 */ 554 public static double[][] loadtxt(File txtFile, int skiprows) throws IOException { 555 requireNonNull(txtFile, "txtFile == null"); 556 return loadtxt(txtFile, "\\s+", skiprows); 557 } 558 559 /** 560 * Read in an optionally GZIP compressed, delimited text file that contains a regular 561 * array of Double values. 562 * 563 * @param txtFile The possibly GZIP compressed table file being read in. 564 * @param delimiter The delimiter to use to separate columns (such as "," for commas, 565 * or "\\s+" for any whitespace. 566 * @param skiprows Indicates the number of rows to skip at the top of the file (to 567 * skip over a header for instance). 568 * @return A 2D double array of the values in the table file. 569 * @throws IOException if there is any problem reading from the file or parsing the 570 * results. 571 */ 572 public static double[][] loadtxt(File txtFile, String delimiter, int skiprows) throws IOException { 573 requireNonNull(txtFile, "txtFile == null"); 574 requireNonNull(delimiter, "delimiter == null"); 575 576 // Deal with bad input to skiprows. 577 if (skiprows < 0) 578 throw new IllegalArgumentException("skiprows must be >= 0"); 579 580 // Read in the lines from the file. 581 List<String> lines = readlines(txtFile); 582 583 int size = lines.size(); 584 int numRows = size - skiprows; 585 if (numRows < 1) 586 return new double[0][0]; 587 588 double[][] output = new double[numRows][]; 589 int col_size = -1; 590 591 // Loop over all the rows in the file. 592 for (int row=0; row < numRows; ++row) { 593 String[] cols = lines.get(row + skiprows).trim().split(delimiter); 594 595 if (col_size < 0) 596 col_size = cols.length; 597 else if (col_size != cols.length) 598 throw new IOException("The number of columns on line " + (row+skiprows+1) + 599 " is different from previous lines."); 600 601 // Allocate memory for this row of the table. 602 double[] row_v = new double[col_size]; 603 output[row] = row_v; 604 605 // Convert all the column strings to floats. 606 for (int col=0; col < col_size; ++col) { 607 row_v[col] = Double.parseDouble(cols[col]); 608 } 609 } 610 611 return output; 612 } 613 614 /** 615 * Create a temporary directory using the specified prefix. 616 * 617 * @param prefix The prefix string to be used in generating the file's name; must be 618 * at least three characters long. May not be null. 619 * @return a reference to a temporary directory using the specified prefix. 620 * @throws java.io.IOException if there is any problem creating the temporary 621 * directory. 622 */ 623 public static File createTempDirectory(String prefix) throws IOException { 624 requireNonNull(prefix, "prefix == null"); 625 626 File tempFile = File.createTempFile(prefix, "", null); 627 if (!tempFile.delete() || !tempFile.mkdir()) 628 throw new IOException("Could not create temporary directory: " + tempFile.getPath()); 629 630 return tempFile; 631 } 632 633 /** 634 * Recursively deletes the directory tree indicated by the specified path. The 635 * directory and all of it's contents are deleted. 636 * 637 * @param path The directory to be deleted. If a plain file is passed in rather than a 638 * directory, it is simply deleted. May not be null. 639 * @return true if the directory and it's contents were successfully deleted. 640 */ 641 public static boolean deleteDirectory(File path) { 642 if (path.exists() && path.isDirectory()) { 643 File[] files = path.listFiles(); 644 for (File file : files) { 645 if (file.isDirectory()) 646 deleteDirectory(file); 647 else 648 file.delete(); 649 } 650 } 651 return (path.delete()); 652 } 653 654 /** 655 * Returns <code>true</code> if and only if the two File objects refer to the same 656 * file in the file system. 657 * 658 * @param f1 The first file to check. May not be null. 659 * @param f2 The 2nd file to check with the 1st one. May not be null. 660 * @return true if and only if the two File objects refer to the same file in the file 661 * system. 662 * @throws IOException If an I/O error occurs, which is possible because the 663 * construction of the canonical pathname may require file system queries. 664 */ 665 public static boolean sameFile(File f1, File f2) throws IOException { 666 return f1.getCanonicalPath().equals(f2.getCanonicalPath()); 667 } 668 669 /** 670 * GZIP compress the src file writing to the destination file. The source & 671 * destination files may be identical. 672 * 673 * @param src The source file to be compressed. May not be null. 674 * @param dst The destination file to compress the source file to. This may be 675 * identical to the source location if the change is to be made in place. 676 * May not be null. 677 * @throws java.io.IOException if there is any problem reading from or writing to the 678 * files. 679 */ 680 public static void gzip(File src, File dst) throws IOException { 681 requireNonNull(src, "src == null"); 682 requireNonNull(dst, "dst = null"); 683 684 File dst2 = dst; 685 if (sameFile(src, dst)) 686 dst2 = File.createTempFile("gzip", null); 687 688 InputStream in = null; 689 OutputStream out = null; 690 try { 691 692 in = new FileInputStream(src); 693 out = new FileOutputStream(dst2); 694 gzip(in, out); 695 696 } finally { 697 if (in != null) 698 in.close(); 699 if (out != null) 700 out.close(); 701 702 if (dst2 != dst) { 703 // If we used a temporary file, move it to the originally 704 // desired location. 705 rename(dst2, dst); 706 707 // Delete the temporary file. 708 dst2.delete(); 709 } 710 } 711 } 712 713 /** 714 * Copy a file to the specified destination directory while GZIP compressing the file. 715 * The original file is not modified. The file in the output directory will have the 716 * same name as the source file, but with ".gz" appended. 717 * 718 * @param src The source file to be compressed. May not be null. 719 * @param destDir The directory to copy the compressed file into. May not be null. 720 * @throws java.io.IOException if there is any problem reading from or writing to the 721 * files. 722 */ 723 public static void copyAndGzip(File src, File destDir) throws IOException { 724 requireNonNull(src, "src == null"); 725 requireNonNull(destDir, "destDir == null"); 726 727 File dst = new File(destDir, src.getName() + ".gz"); 728 729 InputStream in = null; 730 OutputStream out = null; 731 try { 732 733 in = new FileInputStream(src); 734 out = new FileOutputStream(dst); 735 gzip(in, out); 736 737 } finally { 738 if (in != null) 739 in.close(); 740 if (out != null) 741 out.close(); 742 } 743 744 } 745 746 /** 747 * Un-GZIP the compressed src file writing the uncompressed data to the destination 748 * file. The source & destination files may be identical. 749 * 750 * @param src The GZIP compressed source file to be de-compressed. May not be null. 751 * @param dst The destination file to uncompress the source file to. This may be 752 * identical to the source location if the change is to be made in place. 753 * May not be null. 754 * @throws java.io.IOException if there is any problem reading from or writing to the 755 * files. 756 */ 757 public static void ungzip(File src, File dst) throws IOException { 758 requireNonNull(src, "src == null"); 759 requireNonNull(dst, "dst == null"); 760 761 File dst2 = dst; 762 if (sameFile(src, dst)) 763 dst2 = File.createTempFile("gzip", null); 764 765 InputStream in = null; 766 OutputStream out = null; 767 try { 768 769 in = new FileInputStream(src); 770 out = new FileOutputStream(dst2); 771 ungzip(in, out); 772 773 } finally { 774 if (in != null) 775 in.close(); 776 if (out != null) 777 out.close(); 778 779 if (dst2 != dst) { 780 // If we used a temporary file, move it to the originally 781 // desired location. 782 rename(dst2, dst); 783 784 // Delete the temporary file. 785 dst2.delete(); 786 } 787 } 788 } 789 790 /** 791 * Copy a file to the specified destination directory while decompressing the GZIP 792 * file. The source file is not modified. A new file is created in the destination 793 * directory that has the same name as the source file, but without the ".gz" 794 * extension (if there is one). 795 * 796 * @param src The source file GZIP file. May not be null. 797 * @param destDir The directory to copy the uncompressed file into. May not be null. 798 * @throws java.io.IOException if there is any problem reading from or writing to the 799 * files. 800 */ 801 public static void copyAndUngzip(File src, File destDir) throws IOException { 802 requireNonNull(src, "src == null"); 803 requireNonNull(destDir, "destDir == null"); 804 805 String name = src.getName(); 806 File dst; 807 if (name.toLowerCase().endsWith(".gz")) { 808 int len = name.length(); 809 dst = new File(destDir, src.getName().substring(0, len - 3)); 810 } else 811 dst = new File(destDir, name); 812 813 InputStream in = null; 814 OutputStream out = null; 815 try { 816 817 in = new FileInputStream(src); 818 out = new FileOutputStream(dst); 819 ungzip(in, out); 820 821 } finally { 822 if (in != null) 823 in.close(); 824 if (out != null) 825 out.close(); 826 } 827 } 828 829 /** 830 * GZIP compress the input stream and write it to the output stream. 831 * 832 * @param in The source input stream. May not be null. 833 * @param out The destination output stream where GZIP compressed data is to be 834 * written. May not be null. 835 * @throws java.io.IOException if there is any problem reading from or writing to the 836 * streams. 837 */ 838 public static void gzip(InputStream in, OutputStream out) throws IOException { 839 requireNonNull(in, "in == null"); 840 requireNonNull(out, "out == null"); 841 842 GZIPOutputStream gzipOut = new GZIPOutputStream(out); 843 copy(in, gzipOut); 844 gzipOut.finish(); 845 } 846 847 /** 848 * Un-GZIP the compressed input stream and write the uncompressed data to the 849 * specified output stream. 850 * 851 * @param in The source input stream pointing to GZIP compressed data. May not be 852 * null. 853 * @param out The destination output stream where uncompressed data is to be written. 854 * May not be null. 855 * @throws java.io.IOException if there is any problem reading from or writing to the 856 * streams. 857 */ 858 public static void ungzip(InputStream in, OutputStream out) throws IOException { 859 requireNonNull(in, "in == null"); 860 requireNonNull(out, "out == null"); 861 862 GZIPInputStream gzipIn = new GZIPInputStream(in); 863 copy(gzipIn, out); 864 } 865 866 /** 867 * Returns <code>true</code> if the specified input file is pointing at a GZIP 868 * compressed data set. 869 * 870 * @param file The input file to be tested. May not be null. 871 * @return true if the specified input file is pointing at a GZIP compressed data set. 872 * @throws java.io.IOException if there is a problem reading from the specified file. 873 */ 874 public static boolean isGZIPCompressed(File file) throws IOException { 875 requireNonNull(file, "file == null"); 876 try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(file), 4)) { 877 return isGZIPCompressed(is); 878 } 879 } 880 881 /** 882 * Returns <code>true</code> if the specified input stream is pointing at a GZIP 883 * compressed data set. 884 * 885 * @param in The input stream to be tested. May not be null. 886 * @return true if the specified input stream is pointing at a GZIP compressed data 887 * set. 888 * @throws java.io.IOException if there is a problem reading from the input stream. 889 */ 890 public static boolean isGZIPCompressed(BufferedInputStream in) throws IOException { 891 requireNonNull(in, "in == null"); 892 byte[] signature = new byte[2]; 893 894 in.mark(4); 895 in.read(signature); //read the signature 896 in.reset(); 897 898 return isGZIPCompressed(signature); 899 } 900 901 /** 902 * Returns <code>true</code> if the specified array of bytes represent a GZIP 903 * compressed data set. 904 * 905 * @param bytes the array of bytes to be tested. May not be null. 906 * @return true if the specified array of bytes represent a GZIP compressed data set. 907 */ 908 public static boolean isGZIPCompressed(byte[] bytes) { 909 if (bytes.length < 2) { 910 return false; 911 912 } else { 913 return ((bytes[0] == (byte)(GZIPInputStream.GZIP_MAGIC)) 914 && (bytes[1] == (byte)(GZIPInputStream.GZIP_MAGIC >> 8))); 915 } 916 } 917}