1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.io; 18 19 import java.io.File; 20 import java.io.FileFilter; 21 import java.io.FileInputStream; 22 import java.io.FileNotFoundException; 23 import java.io.FileOutputStream; 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.io.OutputStream; 27 import java.net.URL; 28 import java.util.Collection; 29 import java.util.Date; 30 import java.util.Iterator; 31 import java.util.List; 32 import java.util.zip.CRC32; 33 import java.util.zip.CheckedInputStream; 34 import java.util.zip.Checksum; 35 36 import org.apache.commons.io.filefilter.DirectoryFileFilter; 37 import org.apache.commons.io.filefilter.FalseFileFilter; 38 import org.apache.commons.io.filefilter.FileFilterUtils; 39 import org.apache.commons.io.filefilter.IOFileFilter; 40 import org.apache.commons.io.filefilter.SuffixFileFilter; 41 import org.apache.commons.io.filefilter.TrueFileFilter; 42 import org.apache.commons.io.output.NullOutputStream; 43 44 /** 45 * General file manipulation utilities. 46 * <p> 47 * Facilities are provided in the following areas: 48 * <ul> 49 * <li>writing to a file 50 * <li>reading from a file 51 * <li>make a directory including parent directories 52 * <li>copying files and directories 53 * <li>deleting files and directories 54 * <li>converting to and from a URL 55 * <li>listing files and directories by filter and extension 56 * <li>comparing file content 57 * <li>file last changed date 58 * <li>calculating a checksum 59 * </ul> 60 * <p> 61 * Origin of code: Excalibur, Alexandria, Commons-Utils 62 * 63 * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A> 64 * @author <a href="mailto:sanders@apache.org">Scott Sanders</a> 65 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 66 * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a> 67 * @author <a href="mailto:peter@apache.org">Peter Donald</a> 68 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a> 69 * @author Matthew Hawthorne 70 * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a> 71 * @author Stephen Colebourne 72 * @author Ian Springer 73 * @author Chris Eldredge 74 * @author Jim Harrington 75 * @author Niall Pemberton 76 * @author Sandy McArthur 77 * @version $Id: FileUtils.java 507684 2007-02-14 20:38:25Z bayard $ 78 */ 79 public class FileUtils { 80 81 /** 82 * Instances should NOT be constructed in standard programming. 83 */ 84 public FileUtils() { 85 super(); 86 } 87 88 /** 89 * The number of bytes in a kilobyte. 90 */ 91 public static final long ONE_KB = 1024; 92 93 /** 94 * The number of bytes in a megabyte. 95 */ 96 public static final long ONE_MB = ONE_KB * ONE_KB; 97 98 /** 99 * The number of bytes in a gigabyte. 100 */ 101 public static final long ONE_GB = ONE_KB * ONE_MB; 102 103 /** 104 * An empty array of type <code>File</code>. 105 */ 106 public static final File[] EMPTY_FILE_ARRAY = new File[0]; 107 108 //----------------------------------------------------------------------- 109 /** 110 * Opens a {@link FileInputStream} for the specified file, providing better 111 * error messages than simply calling <code>new FileInputStream(file)</code>. 112 * <p> 113 * At the end of the method either the stream will be successfully opened, 114 * or an exception will have been thrown. 115 * <p> 116 * An exception is thrown if the file does not exist. 117 * An exception is thrown if the file object exists but is a directory. 118 * An exception is thrown if the file exists but cannot be read. 119 * 120 * @param file the file to open for input, must not be <code>null</code> 121 * @return a new {@link FileInputStream} for the specified file 122 * @throws FileNotFoundException if the file does not exist 123 * @throws IOException if the file object is a directory 124 * @throws IOException if the file cannot be read 125 * @since Commons IO 1.3 126 */ 127 public static FileInputStream openInputStream(File file) throws IOException { 128 if (file.exists()) { 129 if (file.isDirectory()) { 130 throw new IOException("File '" + file + "' exists but is a directory"); 131 } 132 if (file.canRead() == false) { 133 throw new IOException("File '" + file + "' cannot be read"); 134 } 135 } else { 136 throw new FileNotFoundException("File '" + file + "' does not exist"); 137 } 138 return new FileInputStream(file); 139 } 140 141 //----------------------------------------------------------------------- 142 /** 143 * Opens a {@link FileOutputStream} for the specified file, checking and 144 * creating the parent directory if it does not exist. 145 * <p> 146 * At the end of the method either the stream will be successfully opened, 147 * or an exception will have been thrown. 148 * <p> 149 * The parent directory will be created if it does not exist. 150 * The file will be created if it does not exist. 151 * An exception is thrown if the file object exists but is a directory. 152 * An exception is thrown if the file exists but cannot be written to. 153 * An exception is thrown if the parent directory cannot be created. 154 * 155 * @param file the file to open for output, must not be <code>null</code> 156 * @return a new {@link FileOutputStream} for the specified file 157 * @throws IOException if the file object is a directory 158 * @throws IOException if the file cannot be written to 159 * @throws IOException if a parent directory needs creating but that fails 160 * @since Commons IO 1.3 161 */ 162 public static FileOutputStream openOutputStream(File file) throws IOException { 163 if (file.exists()) { 164 if (file.isDirectory()) { 165 throw new IOException("File '" + file + "' exists but is a directory"); 166 } 167 if (file.canWrite() == false) { 168 throw new IOException("File '" + file + "' cannot be written to"); 169 } 170 } else { 171 File parent = file.getParentFile(); 172 if (parent != null && parent.exists() == false) { 173 if (parent.mkdirs() == false) { 174 throw new IOException("File '" + file + "' could not be created"); 175 } 176 } 177 } 178 return new FileOutputStream(file); 179 } 180 181 //----------------------------------------------------------------------- 182 /** 183 * Returns a human-readable version of the file size, where the input 184 * represents a specific number of bytes. 185 * 186 * @param size the number of bytes 187 * @return a human-readable display value (includes units) 188 */ 189 public static String byteCountToDisplaySize(long size) { 190 String displaySize; 191 192 if (size / ONE_GB > 0) { 193 displaySize = String.valueOf(size / ONE_GB) + " GB"; 194 } else if (size / ONE_MB > 0) { 195 displaySize = String.valueOf(size / ONE_MB) + " MB"; 196 } else if (size / ONE_KB > 0) { 197 displaySize = String.valueOf(size / ONE_KB) + " KB"; 198 } else { 199 displaySize = String.valueOf(size) + " bytes"; 200 } 201 return displaySize; 202 } 203 204 //----------------------------------------------------------------------- 205 /** 206 * Implements the same behaviour as the "touch" utility on Unix. It creates 207 * a new file with size 0 or, if the file exists already, it is opened and 208 * closed without modifying it, but updating the file date and time. 209 * <p> 210 * NOTE: As from v1.3, this method throws an IOException if the last 211 * modified date of the file cannot be set. Also, as from v1.3 this method 212 * creates parent directories if they do not exist. 213 * 214 * @param file the File to touch 215 * @throws IOException If an I/O problem occurs 216 */ 217 public static void touch(File file) throws IOException { 218 if (!file.exists()) { 219 OutputStream out = openOutputStream(file); 220 IOUtils.closeQuietly(out); 221 } 222 boolean success = file.setLastModified(System.currentTimeMillis()); 223 if (!success) { 224 throw new IOException("Unable to set the last modification time for " + file); 225 } 226 } 227 228 //----------------------------------------------------------------------- 229 /** 230 * Converts a Collection containing java.io.File instanced into array 231 * representation. This is to account for the difference between 232 * File.listFiles() and FileUtils.listFiles(). 233 * 234 * @param files a Collection containing java.io.File instances 235 * @return an array of java.io.File 236 */ 237 public static File[] convertFileCollectionToFileArray(Collection files) { 238 return (File[]) files.toArray(new File[files.size()]); 239 } 240 241 //----------------------------------------------------------------------- 242 /** 243 * Finds files within a given directory (and optionally its 244 * subdirectories). All files found are filtered by an IOFileFilter. 245 * 246 * @param files the collection of files found. 247 * @param directory the directory to search in. 248 * @param filter the filter to apply to files and directories. 249 */ 250 private static void innerListFiles(Collection files, File directory, 251 IOFileFilter filter) { 252 File[] found = directory.listFiles((FileFilter) filter); 253 if (found != null) { 254 for (int i = 0; i < found.length; i++) { 255 if (found[i].isDirectory()) { 256 innerListFiles(files, found[i], filter); 257 } else { 258 files.add(found[i]); 259 } 260 } 261 } 262 } 263 264 /** 265 * Finds files within a given directory (and optionally its 266 * subdirectories). All files found are filtered by an IOFileFilter. 267 * <p> 268 * If your search should recurse into subdirectories you can pass in 269 * an IOFileFilter for directories. You don't need to bind a 270 * DirectoryFileFilter (via logical AND) to this filter. This method does 271 * that for you. 272 * <p> 273 * An example: If you want to search through all directories called 274 * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code> 275 * <p> 276 * Another common usage of this method is find files in a directory 277 * tree but ignoring the directories generated CVS. You can simply pass 278 * in <code>FileFilterUtils.makeCVSAware(null)</code>. 279 * 280 * @param directory the directory to search in 281 * @param fileFilter filter to apply when finding files. 282 * @param dirFilter optional filter to apply when finding subdirectories. 283 * If this parameter is <code>null</code>, subdirectories will not be included in the 284 * search. Use TrueFileFilter.INSTANCE to match all directories. 285 * @return an collection of java.io.File with the matching files 286 * @see org.apache.commons.io.filefilter.FileFilterUtils 287 * @see org.apache.commons.io.filefilter.NameFileFilter 288 */ 289 public static Collection listFiles( 290 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 291 if (!directory.isDirectory()) { 292 throw new IllegalArgumentException( 293 "Parameter 'directory' is not a directory"); 294 } 295 if (fileFilter == null) { 296 throw new NullPointerException("Parameter 'fileFilter' is null"); 297 } 298 299 //Setup effective file filter 300 IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter, 301 FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE)); 302 303 //Setup effective directory filter 304 IOFileFilter effDirFilter; 305 if (dirFilter == null) { 306 effDirFilter = FalseFileFilter.INSTANCE; 307 } else { 308 effDirFilter = FileFilterUtils.andFileFilter(dirFilter, 309 DirectoryFileFilter.INSTANCE); 310 } 311 312 //Find files 313 Collection files = new java.util.LinkedList(); 314 innerListFiles(files, directory, 315 FileFilterUtils.orFileFilter(effFileFilter, effDirFilter)); 316 return files; 317 } 318 319 /** 320 * Allows iteration over the files in given directory (and optionally 321 * its subdirectories). 322 * <p> 323 * All files found are filtered by an IOFileFilter. This method is 324 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}. 325 * 326 * @param directory the directory to search in 327 * @param fileFilter filter to apply when finding files. 328 * @param dirFilter optional filter to apply when finding subdirectories. 329 * If this parameter is <code>null</code>, subdirectories will not be included in the 330 * search. Use TrueFileFilter.INSTANCE to match all directories. 331 * @return an iterator of java.io.File for the matching files 332 * @see org.apache.commons.io.filefilter.FileFilterUtils 333 * @see org.apache.commons.io.filefilter.NameFileFilter 334 * @since Commons IO 1.2 335 */ 336 public static Iterator iterateFiles( 337 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 338 return listFiles(directory, fileFilter, dirFilter).iterator(); 339 } 340 341 //----------------------------------------------------------------------- 342 /** 343 * Converts an array of file extensions to suffixes for use 344 * with IOFileFilters. 345 * 346 * @param extensions an array of extensions. Format: {"java", "xml"} 347 * @return an array of suffixes. Format: {".java", ".xml"} 348 */ 349 private static String[] toSuffixes(String[] extensions) { 350 String[] suffixes = new String[extensions.length]; 351 for (int i = 0; i < extensions.length; i++) { 352 suffixes[i] = "." + extensions[i]; 353 } 354 return suffixes; 355 } 356 357 358 /** 359 * Finds files within a given directory (and optionally its subdirectories) 360 * which match an array of extensions. 361 * 362 * @param directory the directory to search in 363 * @param extensions an array of extensions, ex. {"java","xml"}. If this 364 * parameter is <code>null</code>, all files are returned. 365 * @param recursive if true all subdirectories are searched as well 366 * @return an collection of java.io.File with the matching files 367 */ 368 public static Collection listFiles( 369 File directory, String[] extensions, boolean recursive) { 370 IOFileFilter filter; 371 if (extensions == null) { 372 filter = TrueFileFilter.INSTANCE; 373 } else { 374 String[] suffixes = toSuffixes(extensions); 375 filter = new SuffixFileFilter(suffixes); 376 } 377 return listFiles(directory, filter, 378 (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE)); 379 } 380 381 /** 382 * Allows iteration over the files in a given directory (and optionally 383 * its subdirectories) which match an array of extensions. This method 384 * is based on {@link #listFiles(File, String[], boolean)}. 385 * 386 * @param directory the directory to search in 387 * @param extensions an array of extensions, ex. {"java","xml"}. If this 388 * parameter is <code>null</code>, all files are returned. 389 * @param recursive if true all subdirectories are searched as well 390 * @return an iterator of java.io.File with the matching files 391 * @since Commons IO 1.2 392 */ 393 public static Iterator iterateFiles( 394 File directory, String[] extensions, boolean recursive) { 395 return listFiles(directory, extensions, recursive).iterator(); 396 } 397 398 //----------------------------------------------------------------------- 399 /** 400 * Compare the contents of two files to determine if they are equal or not. 401 * <p> 402 * This method checks to see if the two files are different lengths 403 * or if they point to the same file, before resorting to byte-by-byte 404 * comparison of the contents. 405 * <p> 406 * Code origin: Avalon 407 * 408 * @param file1 the first file 409 * @param file2 the second file 410 * @return true if the content of the files are equal or they both don't 411 * exist, false otherwise 412 * @throws IOException in case of an I/O error 413 */ 414 public static boolean contentEquals(File file1, File file2) throws IOException { 415 boolean file1Exists = file1.exists(); 416 if (file1Exists != file2.exists()) { 417 return false; 418 } 419 420 if (!file1Exists) { 421 // two not existing files are equal 422 return true; 423 } 424 425 if (file1.isDirectory() || file2.isDirectory()) { 426 // don't want to compare directory contents 427 throw new IOException("Can't compare directories, only files"); 428 } 429 430 if (file1.length() != file2.length()) { 431 // lengths differ, cannot be equal 432 return false; 433 } 434 435 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 436 // same file 437 return true; 438 } 439 440 InputStream input1 = null; 441 InputStream input2 = null; 442 try { 443 input1 = new FileInputStream(file1); 444 input2 = new FileInputStream(file2); 445 return IOUtils.contentEquals(input1, input2); 446 447 } finally { 448 IOUtils.closeQuietly(input1); 449 IOUtils.closeQuietly(input2); 450 } 451 } 452 453 //----------------------------------------------------------------------- 454 /** 455 * Convert from a <code>URL</code> to a <code>File</code>. 456 * <p> 457 * From version 1.1 this method will decode the URL. 458 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 459 * correctly decoded to <code>/my docs/file.txt</code>. 460 * 461 * @param url the file URL to convert, <code>null</code> returns <code>null</code> 462 * @return the equivalent <code>File</code> object, or <code>null</code> 463 * if the URL's protocol is not <code>file</code> 464 * @throws IllegalArgumentException if the file is incorrectly encoded 465 */ 466 public static File toFile(URL url) { 467 if (url == null || !url.getProtocol().equals("file")) { 468 return null; 469 } else { 470 String filename = url.getFile().replace('/', File.separatorChar); 471 int pos =0; 472 while ((pos = filename.indexOf('%', pos)) >= 0) { 473 if (pos + 2 < filename.length()) { 474 String hexStr = filename.substring(pos + 1, pos + 3); 475 char ch = (char) Integer.parseInt(hexStr, 16); 476 filename = filename.substring(0, pos) + ch + filename.substring(pos + 3); 477 } 478 } 479 return new File(filename); 480 } 481 } 482 483 /** 484 * Converts each of an array of <code>URL</code> to a <code>File</code>. 485 * <p> 486 * Returns an array of the same size as the input. 487 * If the input is <code>null</code>, an empty array is returned. 488 * If the input contains <code>null</code>, the output array contains <code>null</code> at the same 489 * index. 490 * <p> 491 * This method will decode the URL. 492 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 493 * correctly decoded to <code>/my docs/file.txt</code>. 494 * 495 * @param urls the file URLs to convert, <code>null</code> returns empty array 496 * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item 497 * if there was a <code>null</code> at that index in the input array 498 * @throws IllegalArgumentException if any file is not a URL file 499 * @throws IllegalArgumentException if any file is incorrectly encoded 500 * @since Commons IO 1.1 501 */ 502 public static File[] toFiles(URL[] urls) { 503 if (urls == null || urls.length == 0) { 504 return EMPTY_FILE_ARRAY; 505 } 506 File[] files = new File[urls.length]; 507 for (int i = 0; i < urls.length; i++) { 508 URL url = urls[i]; 509 if (url != null) { 510 if (url.getProtocol().equals("file") == false) { 511 throw new IllegalArgumentException( 512 "URL could not be converted to a File: " + url); 513 } 514 files[i] = toFile(url); 515 } 516 } 517 return files; 518 } 519 520 /** 521 * Converts each of an array of <code>File</code> to a <code>URL</code>. 522 * <p> 523 * Returns an array of the same size as the input. 524 * 525 * @param files the files to convert 526 * @return an array of URLs matching the input 527 * @throws IOException if a file cannot be converted 528 */ 529 public static URL[] toURLs(File[] files) throws IOException { 530 URL[] urls = new URL[files.length]; 531 532 for (int i = 0; i < urls.length; i++) { 533 urls[i] = files[i].toURL(); 534 } 535 536 return urls; 537 } 538 539 //----------------------------------------------------------------------- 540 /** 541 * Copies a file to a directory preserving the file date. 542 * <p> 543 * This method copies the contents of the specified source file 544 * to a file of the same name in the specified destination directory. 545 * The destination directory is created if it does not exist. 546 * If the destination file exists, then this method will overwrite it. 547 * 548 * @param srcFile an existing file to copy, must not be <code>null</code> 549 * @param destDir the directory to place the copy in, must not be <code>null</code> 550 * 551 * @throws NullPointerException if source or destination is null 552 * @throws IOException if source or destination is invalid 553 * @throws IOException if an IO error occurs during copying 554 * @see #copyFile(File, File, boolean) 555 */ 556 public static void copyFileToDirectory(File srcFile, File destDir) throws IOException { 557 copyFileToDirectory(srcFile, destDir, true); 558 } 559 560 /** 561 * Copies a file to a directory optionally preserving the file date. 562 * <p> 563 * This method copies the contents of the specified source file 564 * to a file of the same name in the specified destination directory. 565 * The destination directory is created if it does not exist. 566 * If the destination file exists, then this method will overwrite it. 567 * 568 * @param srcFile an existing file to copy, must not be <code>null</code> 569 * @param destDir the directory to place the copy in, must not be <code>null</code> 570 * @param preserveFileDate true if the file date of the copy 571 * should be the same as the original 572 * 573 * @throws NullPointerException if source or destination is <code>null</code> 574 * @throws IOException if source or destination is invalid 575 * @throws IOException if an IO error occurs during copying 576 * @see #copyFile(File, File, boolean) 577 * @since Commons IO 1.3 578 */ 579 public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException { 580 if (destDir == null) { 581 throw new NullPointerException("Destination must not be null"); 582 } 583 if (destDir.exists() && destDir.isDirectory() == false) { 584 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 585 } 586 copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate); 587 } 588 589 /** 590 * Copies a file to a new location preserving the file date. 591 * <p> 592 * This method copies the contents of the specified source file to the 593 * specified destination file. The directory holding the destination file is 594 * created if it does not exist. If the destination file exists, then this 595 * method will overwrite it. 596 * 597 * @param srcFile an existing file to copy, must not be <code>null</code> 598 * @param destFile the new file, must not be <code>null</code> 599 * 600 * @throws NullPointerException if source or destination is <code>null</code> 601 * @throws IOException if source or destination is invalid 602 * @throws IOException if an IO error occurs during copying 603 * @see #copyFileToDirectory(File, File) 604 */ 605 public static void copyFile(File srcFile, File destFile) throws IOException { 606 copyFile(srcFile, destFile, true); 607 } 608 609 /** 610 * Copies a file to a new location. 611 * <p> 612 * This method copies the contents of the specified source file 613 * to the specified destination file. 614 * The directory holding the destination file is created if it does not exist. 615 * If the destination file exists, then this method will overwrite it. 616 * 617 * @param srcFile an existing file to copy, must not be <code>null</code> 618 * @param destFile the new file, must not be <code>null</code> 619 * @param preserveFileDate true if the file date of the copy 620 * should be the same as the original 621 * 622 * @throws NullPointerException if source or destination is <code>null</code> 623 * @throws IOException if source or destination is invalid 624 * @throws IOException if an IO error occurs during copying 625 * @see #copyFileToDirectory(File, File, boolean) 626 */ 627 public static void copyFile(File srcFile, File destFile, 628 boolean preserveFileDate) throws IOException { 629 if (srcFile == null) { 630 throw new NullPointerException("Source must not be null"); 631 } 632 if (destFile == null) { 633 throw new NullPointerException("Destination must not be null"); 634 } 635 if (srcFile.exists() == false) { 636 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 637 } 638 if (srcFile.isDirectory()) { 639 throw new IOException("Source '" + srcFile + "' exists but is a directory"); 640 } 641 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { 642 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); 643 } 644 if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) { 645 if (destFile.getParentFile().mkdirs() == false) { 646 throw new IOException("Destination '" + destFile + "' directory cannot be created"); 647 } 648 } 649 if (destFile.exists() && destFile.canWrite() == false) { 650 throw new IOException("Destination '" + destFile + "' exists but is read-only"); 651 } 652 doCopyFile(srcFile, destFile, preserveFileDate); 653 } 654 655 /** 656 * Internal copy file method. 657 * 658 * @param srcFile the validated source file, must not be <code>null</code> 659 * @param destFile the validated destination file, must not be <code>null</code> 660 * @param preserveFileDate whether to preserve the file date 661 * @throws IOException if an error occurs 662 */ 663 private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException { 664 if (destFile.exists() && destFile.isDirectory()) { 665 throw new IOException("Destination '" + destFile + "' exists but is a directory"); 666 } 667 668 FileInputStream input = new FileInputStream(srcFile); 669 try { 670 FileOutputStream output = new FileOutputStream(destFile); 671 try { 672 IOUtils.copy(input, output); 673 } finally { 674 IOUtils.closeQuietly(output); 675 } 676 } finally { 677 IOUtils.closeQuietly(input); 678 } 679 680 if (srcFile.length() != destFile.length()) { 681 throw new IOException("Failed to copy full contents from '" + 682 srcFile + "' to '" + destFile + "'"); 683 } 684 if (preserveFileDate) { 685 destFile.setLastModified(srcFile.lastModified()); 686 } 687 } 688 689 //----------------------------------------------------------------------- 690 /** 691 * Copies a directory to within another directory preserving the file dates. 692 * <p> 693 * This method copies the source directory and all its contents to a 694 * directory of the same name in the specified destination directory. 695 * <p> 696 * The destination directory is created if it does not exist. 697 * If the destination directory did exist, then this method merges 698 * the source with the destination, with the source taking precedence. 699 * 700 * @param srcDir an existing directory to copy, must not be <code>null</code> 701 * @param destDir the directory to place the copy in, must not be <code>null</code> 702 * 703 * @throws NullPointerException if source or destination is <code>null</code> 704 * @throws IOException if source or destination is invalid 705 * @throws IOException if an IO error occurs during copying 706 * @since Commons IO 1.2 707 */ 708 public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException { 709 if (srcDir == null) { 710 throw new NullPointerException("Source must not be null"); 711 } 712 if (srcDir.exists() && srcDir.isDirectory() == false) { 713 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory"); 714 } 715 if (destDir == null) { 716 throw new NullPointerException("Destination must not be null"); 717 } 718 if (destDir.exists() && destDir.isDirectory() == false) { 719 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 720 } 721 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true); 722 } 723 724 /** 725 * Copies a whole directory to a new location preserving the file dates. 726 * <p> 727 * This method copies the specified directory and all its child 728 * directories and files to the specified destination. 729 * The destination is the new location and name of the directory. 730 * <p> 731 * The destination directory is created if it does not exist. 732 * If the destination directory did exist, then this method merges 733 * the source with the destination, with the source taking precedence. 734 * 735 * @param srcDir an existing directory to copy, must not be <code>null</code> 736 * @param destDir the new directory, must not be <code>null</code> 737 * 738 * @throws NullPointerException if source or destination is <code>null</code> 739 * @throws IOException if source or destination is invalid 740 * @throws IOException if an IO error occurs during copying 741 * @since Commons IO 1.1 742 */ 743 public static void copyDirectory(File srcDir, File destDir) throws IOException { 744 copyDirectory(srcDir, destDir, true); 745 } 746 747 /** 748 * Copies a whole directory to a new location. 749 * <p> 750 * This method copies the contents of the specified source directory 751 * to within the specified destination directory. 752 * <p> 753 * The destination directory is created if it does not exist. 754 * If the destination directory did exist, then this method merges 755 * the source with the destination, with the source taking precedence. 756 * 757 * @param srcDir an existing directory to copy, must not be <code>null</code> 758 * @param destDir the new directory, must not be <code>null</code> 759 * @param preserveFileDate true if the file date of the copy 760 * should be the same as the original 761 * 762 * @throws NullPointerException if source or destination is <code>null</code> 763 * @throws IOException if source or destination is invalid 764 * @throws IOException if an IO error occurs during copying 765 * @since Commons IO 1.1 766 */ 767 public static void copyDirectory(File srcDir, File destDir, 768 boolean preserveFileDate) throws IOException { 769 if (srcDir == null) { 770 throw new NullPointerException("Source must not be null"); 771 } 772 if (destDir == null) { 773 throw new NullPointerException("Destination must not be null"); 774 } 775 if (srcDir.exists() == false) { 776 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 777 } 778 if (srcDir.isDirectory() == false) { 779 throw new IOException("Source '" + srcDir + "' exists but is not a directory"); 780 } 781 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) { 782 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); 783 } 784 doCopyDirectory(srcDir, destDir, preserveFileDate); 785 } 786 787 /** 788 * Internal copy directory method. 789 * 790 * @param srcDir the validated source directory, must not be <code>null</code> 791 * @param destDir the validated destination directory, must not be <code>null</code> 792 * @param preserveFileDate whether to preserve the file date 793 * @throws IOException if an error occurs 794 * @since Commons IO 1.1 795 */ 796 private static void doCopyDirectory(File srcDir, File destDir, boolean preserveFileDate) throws IOException { 797 if (destDir.exists()) { 798 if (destDir.isDirectory() == false) { 799 throw new IOException("Destination '" + destDir + "' exists but is not a directory"); 800 } 801 } else { 802 if (destDir.mkdirs() == false) { 803 throw new IOException("Destination '" + destDir + "' directory cannot be created"); 804 } 805 if (preserveFileDate) { 806 destDir.setLastModified(srcDir.lastModified()); 807 } 808 } 809 if (destDir.canWrite() == false) { 810 throw new IOException("Destination '" + destDir + "' cannot be written to"); 811 } 812 // recurse 813 File[] files = srcDir.listFiles(); 814 if (files == null) { // null if security restricted 815 throw new IOException("Failed to list contents of " + srcDir); 816 } 817 for (int i = 0; i < files.length; i++) { 818 File copiedFile = new File(destDir, files[i].getName()); 819 if (files[i].isDirectory()) { 820 doCopyDirectory(files[i], copiedFile, preserveFileDate); 821 } else { 822 doCopyFile(files[i], copiedFile, preserveFileDate); 823 } 824 } 825 } 826 827 //----------------------------------------------------------------------- 828 /** 829 * Copies bytes from the URL <code>source</code> to a file 830 * <code>destination</code>. The directories up to <code>destination</code> 831 * will be created if they don't already exist. <code>destination</code> 832 * will be overwritten if it already exists. 833 * 834 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code> 835 * @param destination the non-directory <code>File</code> to write bytes to 836 * (possibly overwriting), must not be <code>null</code> 837 * @throws IOException if <code>source</code> URL cannot be opened 838 * @throws IOException if <code>destination</code> is a directory 839 * @throws IOException if <code>destination</code> cannot be written 840 * @throws IOException if <code>destination</code> needs creating but can't be 841 * @throws IOException if an IO error occurs during copying 842 */ 843 public static void copyURLToFile(URL source, File destination) throws IOException { 844 InputStream input = source.openStream(); 845 try { 846 FileOutputStream output = openOutputStream(destination); 847 try { 848 IOUtils.copy(input, output); 849 } finally { 850 IOUtils.closeQuietly(output); 851 } 852 } finally { 853 IOUtils.closeQuietly(input); 854 } 855 } 856 857 //----------------------------------------------------------------------- 858 /** 859 * Recursively delete a directory. 860 * 861 * @param directory directory to delete 862 * @throws IOException in case deletion is unsuccessful 863 */ 864 public static void deleteDirectory(File directory) throws IOException { 865 if (!directory.exists()) { 866 return; 867 } 868 869 cleanDirectory(directory); 870 if (!directory.delete()) { 871 String message = 872 "Unable to delete directory " + directory + "."; 873 throw new IOException(message); 874 } 875 } 876 877 /** 878 * Clean a directory without deleting it. 879 * 880 * @param directory directory to clean 881 * @throws IOException in case cleaning is unsuccessful 882 */ 883 public static void cleanDirectory(File directory) throws IOException { 884 if (!directory.exists()) { 885 String message = directory + " does not exist"; 886 throw new IllegalArgumentException(message); 887 } 888 889 if (!directory.isDirectory()) { 890 String message = directory + " is not a directory"; 891 throw new IllegalArgumentException(message); 892 } 893 894 File[] files = directory.listFiles(); 895 if (files == null) { // null if security restricted 896 throw new IOException("Failed to list contents of " + directory); 897 } 898 899 IOException exception = null; 900 for (int i = 0; i < files.length; i++) { 901 File file = files[i]; 902 try { 903 forceDelete(file); 904 } catch (IOException ioe) { 905 exception = ioe; 906 } 907 } 908 909 if (null != exception) { 910 throw exception; 911 } 912 } 913 914 //----------------------------------------------------------------------- 915 /** 916 * Waits for NFS to propagate a file creation, imposing a timeout. 917 * <p> 918 * This method repeatedly tests {@link File#exists()} until it returns 919 * true up to the maximum time specified in seconds. 920 * 921 * @param file the file to check, must not be <code>null</code> 922 * @param seconds the maximum time in seconds to wait 923 * @return true if file exists 924 * @throws NullPointerException if the file is <code>null</code> 925 */ 926 public static boolean waitFor(File file, int seconds) { 927 int timeout = 0; 928 int tick = 0; 929 while (!file.exists()) { 930 if (tick++ >= 10) { 931 tick = 0; 932 if (timeout++ > seconds) { 933 return false; 934 } 935 } 936 try { 937 Thread.sleep(100); 938 } catch (InterruptedException ignore) { 939 // ignore exception 940 } catch (Exception ex) { 941 break; 942 } 943 } 944 return true; 945 } 946 947 //----------------------------------------------------------------------- 948 /** 949 * Reads the contents of a file into a String. 950 * The file is always closed. 951 * 952 * @param file the file to read, must not be <code>null</code> 953 * @param encoding the encoding to use, <code>null</code> means platform default 954 * @return the file contents, never <code>null</code> 955 * @throws IOException in case of an I/O error 956 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 957 */ 958 public static String readFileToString(File file, String encoding) throws IOException { 959 InputStream in = null; 960 try { 961 in = openInputStream(file); 962 return IOUtils.toString(in, encoding); 963 } finally { 964 IOUtils.closeQuietly(in); 965 } 966 } 967 968 969 /** 970 * Reads the contents of a file into a String using the default encoding for the VM. 971 * The file is always closed. 972 * 973 * @param file the file to read, must not be <code>null</code> 974 * @return the file contents, never <code>null</code> 975 * @throws IOException in case of an I/O error 976 * @since Commons IO 1.3.1 977 */ 978 public static String readFileToString(File file) throws IOException { 979 return readFileToString(file, null); 980 } 981 982 /** 983 * Reads the contents of a file into a byte array. 984 * The file is always closed. 985 * 986 * @param file the file to read, must not be <code>null</code> 987 * @return the file contents, never <code>null</code> 988 * @throws IOException in case of an I/O error 989 * @since Commons IO 1.1 990 */ 991 public static byte[] readFileToByteArray(File file) throws IOException { 992 InputStream in = null; 993 try { 994 in = openInputStream(file); 995 return IOUtils.toByteArray(in); 996 } finally { 997 IOUtils.closeQuietly(in); 998 } 999 } 1000 1001 /** 1002 * Reads the contents of a file line by line to a List of Strings. 1003 * The file is always closed. 1004 * 1005 * @param file the file to read, must not be <code>null</code> 1006 * @param encoding the encoding to use, <code>null</code> means platform default 1007 * @return the list of Strings representing each line in the file, never <code>null</code> 1008 * @throws IOException in case of an I/O error 1009 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1010 * @since Commons IO 1.1 1011 */ 1012 public static List readLines(File file, String encoding) throws IOException { 1013 InputStream in = null; 1014 try { 1015 in = openInputStream(file); 1016 return IOUtils.readLines(in, encoding); 1017 } finally { 1018 IOUtils.closeQuietly(in); 1019 } 1020 } 1021 1022 /** 1023 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. 1024 * The file is always closed. 1025 * 1026 * @param file the file to read, must not be <code>null</code> 1027 * @return the list of Strings representing each line in the file, never <code>null</code> 1028 * @throws IOException in case of an I/O error 1029 * @since Commons IO 1.3 1030 */ 1031 public static List readLines(File file) throws IOException { 1032 return readLines(file, null); 1033 } 1034 1035 /** 1036 * Return an Iterator for the lines in a <code>File</code>. 1037 * <p> 1038 * This method opens an <code>InputStream</code> for the file. 1039 * When you have finished with the iterator you should close the stream 1040 * to free internal resources. This can be done by calling the 1041 * {@link LineIterator#close()} or 1042 * {@link LineIterator#closeQuietly(LineIterator)} method. 1043 * <p> 1044 * The recommended usage pattern is: 1045 * <pre> 1046 * LineIterator it = FileUtils.lineIterator(file, "UTF-8"); 1047 * try { 1048 * while (it.hasNext()) { 1049 * String line = it.nextLine(); 1050 * /// do something with line 1051 * } 1052 * } finally { 1053 * LineIterator.closeQuietly(iterator); 1054 * } 1055 * </pre> 1056 * <p> 1057 * If an exception occurs during the creation of the iterator, the 1058 * underlying stream is closed. 1059 * 1060 * @param file the file to open for input, must not be <code>null</code> 1061 * @param encoding the encoding to use, <code>null</code> means platform default 1062 * @return an Iterator of the lines in the file, never <code>null</code> 1063 * @throws IOException in case of an I/O error (file closed) 1064 * @since Commons IO 1.2 1065 */ 1066 public static LineIterator lineIterator(File file, String encoding) throws IOException { 1067 InputStream in = null; 1068 try { 1069 in = openInputStream(file); 1070 return IOUtils.lineIterator(in, encoding); 1071 } catch (IOException ex) { 1072 IOUtils.closeQuietly(in); 1073 throw ex; 1074 } catch (RuntimeException ex) { 1075 IOUtils.closeQuietly(in); 1076 throw ex; 1077 } 1078 } 1079 1080 /** 1081 * Return an Iterator for the lines in a <code>File</code> using the default encoding for the VM. 1082 * 1083 * @param file the file to open for input, must not be <code>null</code> 1084 * @return an Iterator of the lines in the file, never <code>null</code> 1085 * @throws IOException in case of an I/O error (file closed) 1086 * @since Commons IO 1.3 1087 * @see #lineIterator(File, String) 1088 */ 1089 public static LineIterator lineIterator(File file) throws IOException { 1090 return lineIterator(file, null); 1091 } 1092 1093 //----------------------------------------------------------------------- 1094 /** 1095 * Writes a String to a file creating the file if it does not exist. 1096 * 1097 * NOTE: As from v1.3, the parent directories of the file will be created 1098 * if they do not exist. 1099 * 1100 * @param file the file to write 1101 * @param data the content to write to the file 1102 * @param encoding the encoding to use, <code>null</code> means platform default 1103 * @throws IOException in case of an I/O error 1104 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1105 */ 1106 public static void writeStringToFile(File file, String data, String encoding) throws IOException { 1107 OutputStream out = null; 1108 try { 1109 out = openOutputStream(file); 1110 IOUtils.write(data, out, encoding); 1111 } finally { 1112 IOUtils.closeQuietly(out); 1113 } 1114 } 1115 1116 /** 1117 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1118 * 1119 * @param file the file to write 1120 * @param data the content to write to the file 1121 * @throws IOException in case of an I/O error 1122 */ 1123 public static void writeStringToFile(File file, String data) throws IOException { 1124 writeStringToFile(file, data, null); 1125 } 1126 1127 /** 1128 * Writes a byte array to a file creating the file if it does not exist. 1129 * <p> 1130 * NOTE: As from v1.3, the parent directories of the file will be created 1131 * if they do not exist. 1132 * 1133 * @param file the file to write to 1134 * @param data the content to write to the file 1135 * @throws IOException in case of an I/O error 1136 * @since Commons IO 1.1 1137 */ 1138 public static void writeByteArrayToFile(File file, byte[] data) throws IOException { 1139 OutputStream out = null; 1140 try { 1141 out = openOutputStream(file); 1142 out.write(data); 1143 } finally { 1144 IOUtils.closeQuietly(out); 1145 } 1146 } 1147 1148 /** 1149 * Writes the <code>toString()</code> value of each item in a collection to 1150 * the specified <code>File</code> line by line. 1151 * The specified character encoding and the default line ending will be used. 1152 * <p> 1153 * NOTE: As from v1.3, the parent directories of the file will be created 1154 * if they do not exist. 1155 * 1156 * @param file the file to write to 1157 * @param encoding the encoding to use, <code>null</code> means platform default 1158 * @param lines the lines to write, <code>null</code> entries produce blank lines 1159 * @throws IOException in case of an I/O error 1160 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1161 * @since Commons IO 1.1 1162 */ 1163 public static void writeLines(File file, String encoding, Collection lines) throws IOException { 1164 writeLines(file, encoding, lines, null); 1165 } 1166 1167 /** 1168 * Writes the <code>toString()</code> value of each item in a collection to 1169 * the specified <code>File</code> line by line. 1170 * The default VM encoding and the default line ending will be used. 1171 * 1172 * @param file the file to write to 1173 * @param lines the lines to write, <code>null</code> entries produce blank lines 1174 * @throws IOException in case of an I/O error 1175 * @since Commons IO 1.3 1176 */ 1177 public static void writeLines(File file, Collection lines) throws IOException { 1178 writeLines(file, null, lines, null); 1179 } 1180 1181 /** 1182 * Writes the <code>toString()</code> value of each item in a collection to 1183 * the specified <code>File</code> line by line. 1184 * The specified character encoding and the line ending will be used. 1185 * <p> 1186 * NOTE: As from v1.3, the parent directories of the file will be created 1187 * if they do not exist. 1188 * 1189 * @param file the file to write to 1190 * @param encoding the encoding to use, <code>null</code> means platform default 1191 * @param lines the lines to write, <code>null</code> entries produce blank lines 1192 * @param lineEnding the line separator to use, <code>null</code> is system default 1193 * @throws IOException in case of an I/O error 1194 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1195 * @since Commons IO 1.1 1196 */ 1197 public static void writeLines(File file, String encoding, Collection lines, String lineEnding) throws IOException { 1198 OutputStream out = null; 1199 try { 1200 out = openOutputStream(file); 1201 IOUtils.writeLines(lines, lineEnding, out, encoding); 1202 } finally { 1203 IOUtils.closeQuietly(out); 1204 } 1205 } 1206 1207 /** 1208 * Writes the <code>toString()</code> value of each item in a collection to 1209 * the specified <code>File</code> line by line. 1210 * The default VM encoding and the specified line ending will be used. 1211 * 1212 * @param file the file to write to 1213 * @param lines the lines to write, <code>null</code> entries produce blank lines 1214 * @param lineEnding the line separator to use, <code>null</code> is system default 1215 * @throws IOException in case of an I/O error 1216 * @since Commons IO 1.3 1217 */ 1218 public static void writeLines(File file, Collection lines, String lineEnding) throws IOException { 1219 writeLines(file, null, lines, lineEnding); 1220 } 1221 1222 //----------------------------------------------------------------------- 1223 /** 1224 * Delete a file. If file is a directory, delete it and all sub-directories. 1225 * <p> 1226 * The difference between File.delete() and this method are: 1227 * <ul> 1228 * <li>A directory to be deleted does not have to be empty.</li> 1229 * <li>You get exceptions when a file or directory cannot be deleted. 1230 * (java.io.File methods returns a boolean)</li> 1231 * </ul> 1232 * 1233 * @param file file or directory to delete, must not be <code>null</code> 1234 * @throws NullPointerException if the directory is <code>null</code> 1235 * @throws IOException in case deletion is unsuccessful 1236 */ 1237 public static void forceDelete(File file) throws IOException { 1238 if (file.isDirectory()) { 1239 deleteDirectory(file); 1240 } else { 1241 if (!file.exists()) { 1242 throw new FileNotFoundException("File does not exist: " + file); 1243 } 1244 if (!file.delete()) { 1245 String message = 1246 "Unable to delete file: " + file; 1247 throw new IOException(message); 1248 } 1249 } 1250 } 1251 1252 /** 1253 * Schedule a file to be deleted when JVM exits. 1254 * If file is directory delete it and all sub-directories. 1255 * 1256 * @param file file or directory to delete, must not be <code>null</code> 1257 * @throws NullPointerException if the file is <code>null</code> 1258 * @throws IOException in case deletion is unsuccessful 1259 */ 1260 public static void forceDeleteOnExit(File file) throws IOException { 1261 if (file.isDirectory()) { 1262 deleteDirectoryOnExit(file); 1263 } else { 1264 file.deleteOnExit(); 1265 } 1266 } 1267 1268 /** 1269 * Recursively schedule directory for deletion on JVM exit. 1270 * 1271 * @param directory directory to delete, must not be <code>null</code> 1272 * @throws NullPointerException if the directory is <code>null</code> 1273 * @throws IOException in case deletion is unsuccessful 1274 */ 1275 private static void deleteDirectoryOnExit(File directory) throws IOException { 1276 if (!directory.exists()) { 1277 return; 1278 } 1279 1280 cleanDirectoryOnExit(directory); 1281 directory.deleteOnExit(); 1282 } 1283 1284 /** 1285 * Clean a directory without deleting it. 1286 * 1287 * @param directory directory to clean, must not be <code>null</code> 1288 * @throws NullPointerException if the directory is <code>null</code> 1289 * @throws IOException in case cleaning is unsuccessful 1290 */ 1291 private static void cleanDirectoryOnExit(File directory) throws IOException { 1292 if (!directory.exists()) { 1293 String message = directory + " does not exist"; 1294 throw new IllegalArgumentException(message); 1295 } 1296 1297 if (!directory.isDirectory()) { 1298 String message = directory + " is not a directory"; 1299 throw new IllegalArgumentException(message); 1300 } 1301 1302 File[] files = directory.listFiles(); 1303 if (files == null) { // null if security restricted 1304 throw new IOException("Failed to list contents of " + directory); 1305 } 1306 1307 IOException exception = null; 1308 for (int i = 0; i < files.length; i++) { 1309 File file = files[i]; 1310 try { 1311 forceDeleteOnExit(file); 1312 } catch (IOException ioe) { 1313 exception = ioe; 1314 } 1315 } 1316 1317 if (null != exception) { 1318 throw exception; 1319 } 1320 } 1321 1322 /** 1323 * Make a directory, including any necessary but nonexistent parent 1324 * directories. If there already exists a file with specified name or 1325 * the directory cannot be created then an exception is thrown. 1326 * 1327 * @param directory directory to create, must not be <code>null</code> 1328 * @throws NullPointerException if the directory is <code>null</code> 1329 * @throws IOException if the directory cannot be created 1330 */ 1331 public static void forceMkdir(File directory) throws IOException { 1332 if (directory.exists()) { 1333 if (directory.isFile()) { 1334 String message = 1335 "File " 1336 + directory 1337 + " exists and is " 1338 + "not a directory. Unable to create directory."; 1339 throw new IOException(message); 1340 } 1341 } else { 1342 if (!directory.mkdirs()) { 1343 String message = 1344 "Unable to create directory " + directory; 1345 throw new IOException(message); 1346 } 1347 } 1348 } 1349 1350 //----------------------------------------------------------------------- 1351 /** 1352 * Recursively count size of a directory (sum of the length of all files). 1353 * 1354 * @param directory directory to inspect, must not be <code>null</code> 1355 * @return size of directory in bytes, 0 if directory is security restricted 1356 * @throws NullPointerException if the directory is <code>null</code> 1357 */ 1358 public static long sizeOfDirectory(File directory) { 1359 if (!directory.exists()) { 1360 String message = directory + " does not exist"; 1361 throw new IllegalArgumentException(message); 1362 } 1363 1364 if (!directory.isDirectory()) { 1365 String message = directory + " is not a directory"; 1366 throw new IllegalArgumentException(message); 1367 } 1368 1369 long size = 0; 1370 1371 File[] files = directory.listFiles(); 1372 if (files == null) { // null if security restricted 1373 return 0L; 1374 } 1375 for (int i = 0; i < files.length; i++) { 1376 File file = files[i]; 1377 1378 if (file.isDirectory()) { 1379 size += sizeOfDirectory(file); 1380 } else { 1381 size += file.length(); 1382 } 1383 } 1384 1385 return size; 1386 } 1387 1388 //----------------------------------------------------------------------- 1389 /** 1390 * Tests if the specified <code>File</code> is newer than the reference 1391 * <code>File</code>. 1392 * 1393 * @param file the <code>File</code> of which the modification date must 1394 * be compared, must not be <code>null</code> 1395 * @param reference the <code>File</code> of which the modification date 1396 * is used, must not be <code>null</code> 1397 * @return true if the <code>File</code> exists and has been modified more 1398 * recently than the reference <code>File</code> 1399 * @throws IllegalArgumentException if the file is <code>null</code> 1400 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist 1401 */ 1402 public static boolean isFileNewer(File file, File reference) { 1403 if (reference == null) { 1404 throw new IllegalArgumentException("No specified reference file"); 1405 } 1406 if (!reference.exists()) { 1407 throw new IllegalArgumentException("The reference file '" 1408 + file + "' doesn't exist"); 1409 } 1410 return isFileNewer(file, reference.lastModified()); 1411 } 1412 1413 /** 1414 * Tests if the specified <code>File</code> is newer than the specified 1415 * <code>Date</code>. 1416 * 1417 * @param file the <code>File</code> of which the modification date 1418 * must be compared, must not be <code>null</code> 1419 * @param date the date reference, must not be <code>null</code> 1420 * @return true if the <code>File</code> exists and has been modified 1421 * after the given <code>Date</code>. 1422 * @throws IllegalArgumentException if the file is <code>null</code> 1423 * @throws IllegalArgumentException if the date is <code>null</code> 1424 */ 1425 public static boolean isFileNewer(File file, Date date) { 1426 if (date == null) { 1427 throw new IllegalArgumentException("No specified date"); 1428 } 1429 return isFileNewer(file, date.getTime()); 1430 } 1431 1432 /** 1433 * Tests if the specified <code>File</code> is newer than the specified 1434 * time reference. 1435 * 1436 * @param file the <code>File</code> of which the modification date must 1437 * be compared, must not be <code>null</code> 1438 * @param timeMillis the time reference measured in milliseconds since the 1439 * epoch (00:00:00 GMT, January 1, 1970) 1440 * @return true if the <code>File</code> exists and has been modified after 1441 * the given time reference. 1442 * @throws IllegalArgumentException if the file is <code>null</code> 1443 */ 1444 public static boolean isFileNewer(File file, long timeMillis) { 1445 if (file == null) { 1446 throw new IllegalArgumentException("No specified file"); 1447 } 1448 if (!file.exists()) { 1449 return false; 1450 } 1451 return file.lastModified() > timeMillis; 1452 } 1453 1454 1455 //----------------------------------------------------------------------- 1456 /** 1457 * Tests if the specified <code>File</code> is older than the reference 1458 * <code>File</code>. 1459 * 1460 * @param file the <code>File</code> of which the modification date must 1461 * be compared, must not be <code>null</code> 1462 * @param reference the <code>File</code> of which the modification date 1463 * is used, must not be <code>null</code> 1464 * @return true if the <code>File</code> exists and has been modified before 1465 * the reference <code>File</code> 1466 * @throws IllegalArgumentException if the file is <code>null</code> 1467 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist 1468 */ 1469 public static boolean isFileOlder(File file, File reference) { 1470 if (reference == null) { 1471 throw new IllegalArgumentException("No specified reference file"); 1472 } 1473 if (!reference.exists()) { 1474 throw new IllegalArgumentException("The reference file '" 1475 + file + "' doesn't exist"); 1476 } 1477 return isFileOlder(file, reference.lastModified()); 1478 } 1479 1480 /** 1481 * Tests if the specified <code>File</code> is older than the specified 1482 * <code>Date</code>. 1483 * 1484 * @param file the <code>File</code> of which the modification date 1485 * must be compared, must not be <code>null</code> 1486 * @param date the date reference, must not be <code>null</code> 1487 * @return true if the <code>File</code> exists and has been modified 1488 * before the given <code>Date</code>. 1489 * @throws IllegalArgumentException if the file is <code>null</code> 1490 * @throws IllegalArgumentException if the date is <code>null</code> 1491 */ 1492 public static boolean isFileOlder(File file, Date date) { 1493 if (date == null) { 1494 throw new IllegalArgumentException("No specified date"); 1495 } 1496 return isFileOlder(file, date.getTime()); 1497 } 1498 1499 /** 1500 * Tests if the specified <code>File</code> is older than the specified 1501 * time reference. 1502 * 1503 * @param file the <code>File</code> of which the modification date must 1504 * be compared, must not be <code>null</code> 1505 * @param timeMillis the time reference measured in milliseconds since the 1506 * epoch (00:00:00 GMT, January 1, 1970) 1507 * @return true if the <code>File</code> exists and has been modified before 1508 * the given time reference. 1509 * @throws IllegalArgumentException if the file is <code>null</code> 1510 */ 1511 public static boolean isFileOlder(File file, long timeMillis) { 1512 if (file == null) { 1513 throw new IllegalArgumentException("No specified file"); 1514 } 1515 if (!file.exists()) { 1516 return false; 1517 } 1518 return file.lastModified() < timeMillis; 1519 } 1520 1521 //----------------------------------------------------------------------- 1522 /** 1523 * Computes the checksum of a file using the CRC32 checksum routine. 1524 * The value of the checksum is returned. 1525 * 1526 * @param file the file to checksum, must not be <code>null</code> 1527 * @return the checksum value 1528 * @throws NullPointerException if the file or checksum is <code>null</code> 1529 * @throws IllegalArgumentException if the file is a directory 1530 * @throws IOException if an IO error occurs reading the file 1531 * @since Commons IO 1.3 1532 */ 1533 public static long checksumCRC32(File file) throws IOException { 1534 CRC32 crc = new CRC32(); 1535 checksum(file, crc); 1536 return crc.getValue(); 1537 } 1538 1539 /** 1540 * Computes the checksum of a file using the specified checksum object. 1541 * Multiple files may be checked using one <code>Checksum</code> instance 1542 * if desired simply by reusing the same checksum object. 1543 * For example: 1544 * <pre> 1545 * long csum = FileUtils.checksum(file, new CRC32()).getValue(); 1546 * </pre> 1547 * 1548 * @param file the file to checksum, must not be <code>null</code> 1549 * @param checksum the checksum object to be used, must not be <code>null</code> 1550 * @return the checksum specified, updated with the content of the file 1551 * @throws NullPointerException if the file or checksum is <code>null</code> 1552 * @throws IllegalArgumentException if the file is a directory 1553 * @throws IOException if an IO error occurs reading the file 1554 * @since Commons IO 1.3 1555 */ 1556 public static Checksum checksum(File file, Checksum checksum) throws IOException { 1557 if (file.isDirectory()) { 1558 throw new IllegalArgumentException("Checksums can't be computed on directories"); 1559 } 1560 InputStream in = null; 1561 try { 1562 in = new CheckedInputStream(new FileInputStream(file), checksum); 1563 IOUtils.copy(in, new NullOutputStream()); 1564 } finally { 1565 IOUtils.closeQuietly(in); 1566 } 1567 return checksum; 1568 } 1569 1570 }