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 February 23, 2025 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 * Sets the Java "user.dir" environment variable to the absolute path of the 142 * specified directory name. If the specified directory doesn't exist, this 143 * function tries to create it before setting the environment variable to it. 144 * 145 * @param directory_name The directory name to set the "user.dir" to. 146 * @return Returns true if the directory was set and false if it was not. 147 * @see getCurrentDirectory 148 */ 149 public static boolean setCurrentDirectory(String directory_name) { 150 boolean result = false; // Boolean indicating whether directory was set 151 152 File directory = new File(directory_name).getAbsoluteFile(); 153 if (directory.exists() || directory.mkdirs()) { 154 result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null); 155 } 156 157 return result; 158 } 159 160 /** 161 * Return the current working directory as defined by the "user.dir" environment 162 * variable as a File object. 163 * 164 * @return A File object representing the "user.dir" directory. 165 * @see setCurrentDirectory 166 */ 167 public static File getCurrentDirectory() { 168 String path = System.getProperty("user.dir"); 169 return new File(path).getAbsoluteFile(); 170 } 171 172 /** 173 * Copy a file from the source to the destination locations. 174 * 175 * @param src The source file. May not be null. 176 * @param dst The destination file to copy the source file to. May not be null. 177 * @throws java.io.IOException if there is any problem reading from or writing to the 178 * files. 179 */ 180 public static void copy(File src, File dst) throws IOException { 181 requireNonNull(src, "src == null"); 182 requireNonNull(dst, "dst == null"); 183 184 // If src and dst are the same; hence no copying is required. 185 if (sameFile(src, dst)) 186 return; 187 188 InputStream in = null; 189 OutputStream out = null; 190 try { 191 192 in = new FileInputStream(src); 193 out = new FileOutputStream(dst); 194 copy(in, out); 195 196 } finally { 197 if (in != null) 198 in.close(); 199 if (out != null) 200 out.close(); 201 } 202 } 203 204 /** 205 * Copy the input stream to the output stream. 206 * 207 * @param in The source input stream. May not be null. 208 * @param out The destination output stream. May not be null. 209 * @throws java.io.IOException if there is any problem reading from or writing to the 210 * streams. 211 */ 212 public static void copy(InputStream in, OutputStream out) throws IOException { 213 requireNonNull(in, "in == null"); 214 requireNonNull(out, "out == null"); 215 216 byte[] buf = new byte[1024]; 217 int len; 218 while ((len = in.read(buf)) > 0) { 219 out.write(buf, 0, len); 220 } 221 } 222 223 /** 224 * Recursively copy the contents of an entire directory tree from source to 225 * destination. This also works if a single file is passed as the source and 226 * destination. 227 * 228 * @param source The source directory or file to copy. May not be null. 229 * @param destination The destination directory or file. May not be null. 230 * @throws java.io.IOException If there is any problem copying the directories or 231 * files. 232 */ 233 public static void copyDirectory(File source, File destination) throws IOException { 234 if (source.isDirectory()) { 235 if (!destination.exists()) { 236 boolean success = destination.mkdirs(); 237 if (!success) 238 throw new IOException("Could not create directory " + destination); 239 } 240 241 String files[] = source.list(); 242 243 for (String file : files) { 244 File srcFile = new File(source, file); 245 File destFile = new File(destination, file); 246 247 copyDirectory(srcFile, destFile); 248 } 249 } else { 250 copy(source, destination); 251 } 252 } 253 254 /** 255 * Attempt to rename a file from the source File location to the destination File 256 * location. If the standard atomic rename fails, this method falls back on copying 257 * the file which is dangerous as it is not atomic. The fall back will not work if the 258 * source or destination is a directory and an exception is thrown in that case. 259 * 260 * @param src The source file to be renamed. Upon exit, this object MAY point to the 261 * same location as "dst" or it may be left unchanged. Either way, barring 262 * an exception, the file will no longer exist at the input src path 263 * location. May not be null. 264 * @param dst The destination path to rename the source file to. May not be null. 265 * @throws java.io.IOException if there is any problem renaming the file. 266 */ 267 public static void rename(File src, File dst) throws IOException { 268 requireNonNull(dst, "dst == null"); 269 270 if (!src.exists()) 271 throw new FileNotFoundException("\"" + src.getPath() + "\" not found."); 272 273 // If the source and destination are the same, no rename is required. 274 if (sameFile(src, dst)) 275 return; 276 277 // Make sure we can write to the destination location. 278 if (dst.exists()) { 279 // The file already exists, can we write to it? 280 if (!dst.canWrite()) 281 throw new IOException("Can not write to \"" + dst.getPath() + "\"."); 282 283 } else { 284 // The file does not already exist. Can we create a file here? 285 boolean success = dst.createNewFile(); 286 if (!success) 287 throw new IOException("Can not write to \"" + dst.getPath() + "\"."); 288 dst.delete(); 289 } 290 291 try { 292 // Try to use the atomic rename first. 293 boolean success = src.renameTo(dst); 294 if (!success) { 295 // Delete the destination file, if it exists. Then wait a second and try again. 296 if (dst.exists()) 297 dst.delete(); 298 Thread.sleep(1000); 299 success = src.renameTo(dst); 300 301 if (!success) { 302 // If renaming the file doesn't work, fall back on copying it. 303 // This doesn't work for directories. 304 if (!src.isFile()) 305 throw new IOException("Move failed, source is dir: \"" + src.getPath() + "\"."); 306 if (dst.isDirectory()) 307 throw new IOException("Move failed, destination is dir: \"" + dst.getPath() + "\"."); 308 copy(src, dst); 309 src.delete(); 310 } 311 } 312 } catch (InterruptedException e) { 313 throw new InterruptedIOException(); 314 } 315 } 316 317 /** 318 * Returns a list of String objects each of which represents a line in the specified 319 * file. The file may optionally be GZIP compressed. 320 * 321 * @param file The possibly GZIP compressed file to be read in. May not be null. 322 * @return A list of String objects, one for each line in the specified file. 323 * @throws java.io.IOException if there is any problem reading from the file. 324 */ 325 public static List<String> readlines(File file) throws IOException { 326 requireNonNull(file, "file == null"); 327 FileInputStream instream = new FileInputStream(file); 328 return readlines(instream); 329 } 330 331 /** 332 * Returns a list of String objects each of which represents a line in the specified 333 * input stream. The input stream may optionally be GZIP compressed. 334 * 335 * @param instream The input stream to be read in. May optionally be GZIP compressed. 336 * May not be null. 337 * @return A list of String objects, one for each line in the specified input stream. 338 * @throws java.io.IOException if there is any problem reading from the stream. 339 */ 340 public static List<String> readlines(InputStream instream) throws IOException { 341 requireNonNull(instream, "instream == null"); 342 343 // Deal with the possibility that the input stream is GZIP compressed. 344 InputStream in = new BufferedInputStream(instream); 345 if (isGZIPCompressed((BufferedInputStream)in)) 346 in = new GZIPInputStream(in); 347 348 List<String> output = new ArrayList(); 349 try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { 350 String aLine; 351 do { 352 aLine = reader.readLine(); 353 if (aLine != null) 354 output.add(aLine); 355 356 } while (aLine != null); 357 358 } 359 360 return output; 361 } 362 363 /** 364 * Write out a list of String objects which represent a line to the specified output 365 * stream. A line ending character is appended to each line. 366 * 367 * @param outFile The path to the output file to be written to. May not be null. 368 * @param lines The list of String objects to be written out. May not be null. 369 * @throws IOException if there is any problem writing to the output file. 370 */ 371 public static void writelines(String outFile, List<String> lines) throws IOException { 372 requireNonNull(outFile, "outFile == null"); 373 requireNonNull(lines, "lines == null"); 374 375 File file = new File(outFile); 376 writelines(file, lines); 377 } 378 379 /** 380 * Write out a list of String objects which represent a line to the specified output 381 * stream. A line ending character is appended to each line. 382 * 383 * @param outFile The output file to be written to. May not be null. 384 * @param lines The list of String objects to be written out. May not be null. 385 * @throws IOException if there is any problem writing to the output file. 386 */ 387 public static void writelines(File outFile, List<String> lines) throws IOException { 388 requireNonNull(outFile, "outFile == null"); 389 requireNonNull(lines, "lines == null"); 390 391 try (FileOutputStream outstream = new FileOutputStream(outFile)) { 392 writelines(outstream, lines); 393 } 394 } 395 396 /** 397 * Write out a list of String objects which represent a line to the specified output 398 * stream. A line ending character is appended to each line. 399 * 400 * @param outstream The output stream to be written to. May not be null. 401 * @param lines The list of String objects to be written out. May not be null. 402 * @throws IOException if there is any problem writing to the output stream. 403 */ 404 public static void writelines(OutputStream outstream, List<String> lines) throws IOException { 405 requireNonNull(outstream, "outstream == null"); 406 requireNonNull(lines, "lines == null"); 407 408 try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outstream))) { 409 410 // Write out the lines to the output file. 411 int size = lines.size(); 412 for (int i = 0; i < size; ++i) { 413 writer.write(lines.get(i)); 414 writer.newLine(); 415 } 416 417 } 418 419 } 420 421 /** 422 * Returns a buffer that contains the contents of the specified file. 423 * 424 * @param file The file to be read into a new ByteBuffer. May not be null. 425 * @return The buffer containing the contents of the specified file. 426 * @throws java.io.IOException if there is any problem reading from the file. 427 */ 428 public static byte[] file2Buffer(File file) throws IOException { 429 430 // Read the file into a byte buffer. 431 if (file.length() > (long)Integer.MAX_VALUE) 432 throw new IOException(file.getName() + " is to large to convert into a ByteBuffer!"); 433 434 int fileSize = (int)file.length(); 435 byte[] buffer = new byte[fileSize]; 436 try (FileInputStream fis = new FileInputStream(file)) { 437 fis.read(buffer, 0, fileSize); 438 } 439 440 return buffer; 441 } 442 443 /** 444 * Write the entire contents of a byte array to the specified file. 445 * 446 * @param buffer The buffer to be written to the file. May not be null. 447 * @param file The file to write the buffer to (it's existing contents will be 448 * overwritten). May not be null. 449 * @throws java.io.IOException if there is any problem writing to the file. 450 */ 451 public static void buffer2File(byte[] buffer, File file) throws IOException { 452 requireNonNull(buffer, "buffer == null"); 453 requireNonNull(file, "file == null"); 454 455 try (FileOutputStream fos = new FileOutputStream(file)) { 456 fos.write(buffer); 457 } 458 459 } 460 461 /** 462 * Returns a ByteBuffer that contains the contents of the specified file. 463 * 464 * @param file The file to be read into a new ByteBuffer. May not be null. 465 * @return The ByteBuffer containing the contents of the specified file. 466 * @throws java.io.IOException if there is any problem reading from the file. 467 */ 468 public static ByteBuffer file2ByteBuffer(File file) throws IOException { 469 470 // Read the file into a byte buffer. 471 if (file.length() > Integer.MAX_VALUE) 472 throw new IOException(file.getName() + " is to large to convert into a ByteBuffer!"); 473 474 int fileSize = (int)file.length(); 475 byte[] mybytearray = new byte[fileSize]; 476 try (FileInputStream fis = new FileInputStream(file)) { 477 fis.read(mybytearray, 0, fileSize); 478 } 479 480 ByteBuffer buffer = ByteBuffer.wrap(mybytearray); 481 482 return buffer; 483 } 484 485 /** 486 * Write the contents of a ByteBuffer, from the beginning up to the current position, 487 * to the specified file. 488 * 489 * @param buffer The buffer to be written to the file. May not be null. 490 * @param file The file to write the buffer to (it's existing contents will be 491 * overwritten). May not be null. 492 * @throws java.io.IOException if there is any problem writing the byte buffer to the 493 * file. 494 */ 495 public static void byteBuffer2File(ByteBuffer buffer, File file) throws IOException { 496 requireNonNull(file); 497 int pos = buffer.position(); 498 buffer.flip(); 499 byte[] mybytearray = new byte[buffer.limit()]; 500 buffer.get(mybytearray); 501 502 try (FileOutputStream fos = new FileOutputStream(file)) { 503 fos.write(mybytearray); 504 } 505 506 buffer.position(pos); 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 * @return A 2D double array of the values in the table file. 516 * @throws IOException if there is any problem reading from the file or parsing the 517 * results. 518 */ 519 public static double[][] loadtxt(String filePath) throws IOException { 520 requireNonNull(filePath, "filePath == null"); 521 return loadtxt(new File(filePath), "\\s+", 0); 522 } 523 524 /** 525 * Read in an optionally GZIP compressed, delimited text file that contains a regular 526 * array of Double values. The delimiter may be any whitespace. 527 * 528 * @param filePath Path to/Name of the possibly GZIP compressed table file being read 529 * in. 530 * @param skiprows Indicates the number of rows to skip at the top of the file (to 531 * skip over a header for instance). 532 * @return A 2D double array of the values in the table file. 533 * @throws IOException if there is any problem reading from the file or parsing the 534 * results. 535 */ 536 public static double[][] loadtxt(String filePath, int skiprows) throws IOException { 537 requireNonNull(filePath, "filePath == null"); 538 return loadtxt(new File(filePath), "\\s+", skiprows); 539 } 540 541 /** 542 * Read in an optionally GZIP compressed, delimited text file that contains a regular 543 * array of Double values. The delimiter may be any whitespace. 544 * 545 * @param filePath Path to/Name of the possibly GZIP compressed table file being read 546 * in. 547 * @param delimiter The delimiter to use to separate columns (such as "," for commas, 548 * or "\\s+" for any whitespace. 549 * @param skiprows Indicates the number of rows to skip at the top of the file (to 550 * skip over a header for instance). 551 * @return A 2D double array of the values in the table file. 552 * @throws IOException if there is any problem reading from the file or parsing the 553 * results. 554 */ 555 public static double[][] loadtxt(String filePath, String delimiter, int skiprows) throws IOException { 556 requireNonNull(filePath, "filePath == null"); 557 requireNonNull(delimiter, "delimiter == null"); 558 return loadtxt(new File(filePath), delimiter, skiprows); 559 } 560 561 /** 562 * Read in an optionally GZIP compressed, delimited text file that contains a regular 563 * array of Double values. The delimiter may be any whitespace. 564 * 565 * @param txtFile The possibly GZIP compressed table file being read in. 566 * @return A 2D double array of the values in the table file. 567 * @throws IOException if there is any problem reading from the file or parsing the 568 * results. 569 */ 570 public static double[][] loadtxt(File txtFile) throws IOException { 571 requireNonNull(txtFile, "txtFile == null"); 572 return loadtxt(txtFile, "\\s+", 0); 573 } 574 575 /** 576 * Read in an optionally GZIP compressed, delimited text file that contains a regular 577 * array of Double values. The delimiter may be any whitespace. 578 * 579 * @param txtFile The possibly GZIP compressed table file being read in. 580 * @param skiprows Indicates the number of rows to skip at the top of the file (to 581 * skip over a header for instance). 582 * @return A 2D double array of the values in the table file. 583 * @throws IOException if there is any problem reading from the file or parsing the 584 * results. 585 */ 586 public static double[][] loadtxt(File txtFile, int skiprows) throws IOException { 587 requireNonNull(txtFile, "txtFile == null"); 588 return loadtxt(txtFile, "\\s+", skiprows); 589 } 590 591 /** 592 * Read in an optionally GZIP compressed, delimited text file that contains a regular 593 * array of Double values. 594 * 595 * @param txtFile The possibly GZIP compressed table file being read in. 596 * @param delimiter The delimiter to use to separate columns (such as "," for commas, 597 * or "\\s+" for any whitespace. 598 * @param skiprows Indicates the number of rows to skip at the top of the file (to 599 * skip over a header for instance). 600 * @return A 2D double array of the values in the table file. 601 * @throws IOException if there is any problem reading from the file or parsing the 602 * results. 603 */ 604 public static double[][] loadtxt(File txtFile, String delimiter, int skiprows) throws IOException { 605 requireNonNull(txtFile, "txtFile == null"); 606 requireNonNull(delimiter, "delimiter == null"); 607 608 // Deal with bad input to skiprows. 609 if (skiprows < 0) 610 throw new IllegalArgumentException("skiprows must be >= 0"); 611 612 // Read in the lines from the file. 613 List<String> lines = readlines(txtFile); 614 615 int size = lines.size(); 616 int numRows = size - skiprows; 617 if (numRows < 1) 618 return new double[0][0]; 619 620 double[][] output = new double[numRows][]; 621 int col_size = -1; 622 623 // Loop over all the rows in the file. 624 for (int row=0; row < numRows; ++row) { 625 String[] cols = lines.get(row + skiprows).trim().split(delimiter); 626 627 if (col_size < 0) 628 col_size = cols.length; 629 else if (col_size != cols.length) 630 throw new IOException("The number of columns on line " + (row+skiprows+1) + 631 " is different from previous lines."); 632 633 // Allocate memory for this row of the table. 634 double[] row_v = new double[col_size]; 635 output[row] = row_v; 636 637 // Convert all the column strings to floats. 638 for (int col=0; col < col_size; ++col) { 639 row_v[col] = Double.parseDouble(cols[col]); 640 } 641 } 642 643 return output; 644 } 645 646 /** 647 * Create a temporary directory using the specified prefix. 648 * 649 * @param prefix The prefix string to be used in generating the file's name; must be 650 * at least three characters long. May not be null. 651 * @return a reference to a temporary directory using the specified prefix. 652 * @throws java.io.IOException if there is any problem creating the temporary 653 * directory. 654 */ 655 public static File createTempDirectory(String prefix) throws IOException { 656 requireNonNull(prefix, "prefix == null"); 657 658 File tempFile = File.createTempFile(prefix, "", null); 659 if (!tempFile.delete() || !tempFile.mkdir()) 660 throw new IOException("Could not create temporary directory: " + tempFile.getPath()); 661 662 return tempFile; 663 } 664 665 /** 666 * Recursively deletes the directory tree indicated by the specified path. The 667 * directory and all of it's contents are deleted. 668 * 669 * @param path The directory to be deleted. If a plain file is passed in rather than a 670 * directory, it is simply deleted. May not be null. 671 * @return true if the directory and it's contents were successfully deleted. 672 */ 673 public static boolean deleteDirectory(File path) { 674 if (path.exists() && path.isDirectory()) { 675 File[] files = path.listFiles(); 676 for (File file : files) { 677 if (file.isDirectory()) 678 deleteDirectory(file); 679 else 680 file.delete(); 681 } 682 } 683 return (path.delete()); 684 } 685 686 /** 687 * Returns <code>true</code> if and only if the two File objects refer to the same 688 * file in the file system. 689 * 690 * @param f1 The first file to check. May not be null. 691 * @param f2 The 2nd file to check with the 1st one. May not be null. 692 * @return true if and only if the two File objects refer to the same file in the file 693 * system. 694 * @throws IOException If an I/O error occurs, which is possible because the 695 * construction of the canonical pathname may require file system queries. 696 */ 697 public static boolean sameFile(File f1, File f2) throws IOException { 698 return f1.getCanonicalPath().equals(f2.getCanonicalPath()); 699 } 700 701 /** 702 * GZIP compress the src file writing to the destination file. The source & 703 * destination files may be identical. 704 * 705 * @param src The source file to be compressed. May not be null. 706 * @param dst The destination file to compress the source file to. This may be 707 * identical to the source location if the change is to be made in place. 708 * May not be null. 709 * @throws java.io.IOException if there is any problem reading from or writing to the 710 * files. 711 */ 712 public static void gzip(File src, File dst) throws IOException { 713 requireNonNull(src, "src == null"); 714 requireNonNull(dst, "dst = null"); 715 716 File dst2 = dst; 717 if (sameFile(src, dst)) 718 dst2 = File.createTempFile("gzip", null); 719 720 InputStream in = null; 721 OutputStream out = null; 722 try { 723 724 in = new FileInputStream(src); 725 out = new FileOutputStream(dst2); 726 gzip(in, out); 727 728 } finally { 729 if (in != null) 730 in.close(); 731 if (out != null) 732 out.close(); 733 734 if (dst2 != dst) { 735 // If we used a temporary file, move it to the originally 736 // desired location. 737 rename(dst2, dst); 738 739 // Delete the temporary file. 740 dst2.delete(); 741 } 742 } 743 } 744 745 /** 746 * Copy a file to the specified destination directory while GZIP compressing the file. 747 * The original file is not modified. The file in the output directory will have the 748 * same name as the source file, but with ".gz" appended. 749 * 750 * @param src The source file to be compressed. May not be null. 751 * @param destDir The directory to copy the compressed file into. May not be null. 752 * @throws java.io.IOException if there is any problem reading from or writing to the 753 * files. 754 */ 755 public static void copyAndGzip(File src, File destDir) throws IOException { 756 requireNonNull(src, "src == null"); 757 requireNonNull(destDir, "destDir == null"); 758 759 File dst = new File(destDir, src.getName() + ".gz"); 760 761 InputStream in = null; 762 OutputStream out = null; 763 try { 764 765 in = new FileInputStream(src); 766 out = new FileOutputStream(dst); 767 gzip(in, out); 768 769 } finally { 770 if (in != null) 771 in.close(); 772 if (out != null) 773 out.close(); 774 } 775 776 } 777 778 /** 779 * Un-GZIP the compressed src file writing the uncompressed data to the destination 780 * file. The source & destination files may be identical. 781 * 782 * @param src The GZIP compressed source file to be de-compressed. May not be null. 783 * @param dst The destination file to uncompress the source file to. This may be 784 * identical to the source location if the change is to be made in place. 785 * May not be null. 786 * @throws java.io.IOException if there is any problem reading from or writing to the 787 * files. 788 */ 789 public static void ungzip(File src, File dst) throws IOException { 790 requireNonNull(src, "src == null"); 791 requireNonNull(dst, "dst == null"); 792 793 File dst2 = dst; 794 if (sameFile(src, dst)) 795 dst2 = File.createTempFile("gzip", null); 796 797 InputStream in = null; 798 OutputStream out = null; 799 try { 800 801 in = new FileInputStream(src); 802 out = new FileOutputStream(dst2); 803 ungzip(in, out); 804 805 } finally { 806 if (in != null) 807 in.close(); 808 if (out != null) 809 out.close(); 810 811 if (dst2 != dst) { 812 // If we used a temporary file, move it to the originally 813 // desired location. 814 rename(dst2, dst); 815 816 // Delete the temporary file. 817 dst2.delete(); 818 } 819 } 820 } 821 822 /** 823 * Copy a file to the specified destination directory while decompressing the GZIP 824 * file. The source file is not modified. A new file is created in the destination 825 * directory that has the same name as the source file, but without the ".gz" 826 * extension (if there is one). 827 * 828 * @param src The source file GZIP file. May not be null. 829 * @param destDir The directory to copy the uncompressed file into. May not be null. 830 * @throws java.io.IOException if there is any problem reading from or writing to the 831 * files. 832 */ 833 public static void copyAndUngzip(File src, File destDir) throws IOException { 834 requireNonNull(src, "src == null"); 835 requireNonNull(destDir, "destDir == null"); 836 837 String name = src.getName(); 838 File dst; 839 if (name.toLowerCase().endsWith(".gz")) { 840 int len = name.length(); 841 dst = new File(destDir, src.getName().substring(0, len - 3)); 842 } else 843 dst = new File(destDir, name); 844 845 InputStream in = null; 846 OutputStream out = null; 847 try { 848 849 in = new FileInputStream(src); 850 out = new FileOutputStream(dst); 851 ungzip(in, out); 852 853 } finally { 854 if (in != null) 855 in.close(); 856 if (out != null) 857 out.close(); 858 } 859 } 860 861 /** 862 * GZIP compress the input stream and write it to the output stream. 863 * 864 * @param in The source input stream. May not be null. 865 * @param out The destination output stream where GZIP compressed data is to be 866 * written. May not be null. 867 * @throws java.io.IOException if there is any problem reading from or writing to the 868 * streams. 869 */ 870 public static void gzip(InputStream in, OutputStream out) throws IOException { 871 requireNonNull(in, "in == null"); 872 requireNonNull(out, "out == null"); 873 874 GZIPOutputStream gzipOut = new GZIPOutputStream(out); 875 copy(in, gzipOut); 876 gzipOut.finish(); 877 } 878 879 /** 880 * Un-GZIP the compressed input stream and write the uncompressed data to the 881 * specified output stream. 882 * 883 * @param in The source input stream pointing to GZIP compressed data. May not be 884 * null. 885 * @param out The destination output stream where uncompressed data is to be written. 886 * May not be null. 887 * @throws java.io.IOException if there is any problem reading from or writing to the 888 * streams. 889 */ 890 public static void ungzip(InputStream in, OutputStream out) throws IOException { 891 requireNonNull(in, "in == null"); 892 requireNonNull(out, "out == null"); 893 894 GZIPInputStream gzipIn = new GZIPInputStream(in); 895 copy(gzipIn, out); 896 } 897 898 /** 899 * Returns <code>true</code> if the specified input file is pointing at a GZIP 900 * compressed data set. 901 * 902 * @param file The input file to be tested. May not be null. 903 * @return true if the specified input file is pointing at a GZIP compressed data set. 904 * @throws java.io.IOException if there is a problem reading from the specified file. 905 */ 906 public static boolean isGZIPCompressed(File file) throws IOException { 907 requireNonNull(file, "file == null"); 908 try (BufferedInputStream is = new BufferedInputStream(new FileInputStream(file), 4)) { 909 return isGZIPCompressed(is); 910 } 911 } 912 913 /** 914 * Returns <code>true</code> if the specified input stream is pointing at a GZIP 915 * compressed data set. 916 * 917 * @param in The input stream to be tested. May not be null. 918 * @return true if the specified input stream is pointing at a GZIP compressed data 919 * set. 920 * @throws java.io.IOException if there is a problem reading from the input stream. 921 */ 922 public static boolean isGZIPCompressed(BufferedInputStream in) throws IOException { 923 requireNonNull(in, "in == null"); 924 byte[] signature = new byte[2]; 925 926 in.mark(4); 927 in.read(signature); //read the signature 928 in.reset(); 929 930 return isGZIPCompressed(signature); 931 } 932 933 /** 934 * Returns <code>true</code> if the specified array of bytes represent a GZIP 935 * compressed data set. 936 * 937 * @param bytes the array of bytes to be tested. May not be null. 938 * @return true if the specified array of bytes represent a GZIP compressed data set. 939 */ 940 public static boolean isGZIPCompressed(byte[] bytes) { 941 if (bytes.length < 2) { 942 return false; 943 944 } else { 945 return ((bytes[0] == (byte)(GZIPInputStream.GZIP_MAGIC)) 946 && (bytes[1] == (byte)(GZIPInputStream.GZIP_MAGIC >> 8))); 947 } 948 } 949}