001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io; 018 019import java.io.BufferedOutputStream; 020import java.io.File; 021import java.io.FileFilter; 022import java.io.FileInputStream; 023import java.io.FileNotFoundException; 024import java.io.FileOutputStream; 025import java.io.IOException; 026import java.io.InputStream; 027import java.io.InputStreamReader; 028import java.io.OutputStream; 029import java.io.Reader; 030import java.io.UnsupportedEncodingException; 031import java.math.BigInteger; 032import java.net.URL; 033import java.net.URLConnection; 034import java.nio.ByteBuffer; 035import java.nio.channels.FileChannel; 036import java.nio.charset.Charset; 037import java.nio.charset.UnsupportedCharsetException; 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.Date; 041import java.util.Iterator; 042import java.util.List; 043import java.util.zip.CRC32; 044import java.util.zip.CheckedInputStream; 045import java.util.zip.Checksum; 046 047import org.apache.commons.io.filefilter.DirectoryFileFilter; 048import org.apache.commons.io.filefilter.FalseFileFilter; 049import org.apache.commons.io.filefilter.FileFilterUtils; 050import org.apache.commons.io.filefilter.IOFileFilter; 051import org.apache.commons.io.filefilter.SuffixFileFilter; 052import org.apache.commons.io.filefilter.TrueFileFilter; 053import org.apache.commons.io.output.NullOutputStream; 054 055/** 056 * General file manipulation utilities. 057 * <p> 058 * Facilities are provided in the following areas: 059 * <ul> 060 * <li>writing to a file 061 * <li>reading from a file 062 * <li>make a directory including parent directories 063 * <li>copying files and directories 064 * <li>deleting files and directories 065 * <li>converting to and from a URL 066 * <li>listing files and directories by filter and extension 067 * <li>comparing file content 068 * <li>file last changed date 069 * <li>calculating a checksum 070 * </ul> 071 * <p> 072 * Origin of code: Excalibur, Alexandria, Commons-Utils 073 * 074 * @version $Id: FileUtils.java 1480346 2013-05-08 16:45:03Z ggregory $ 075 */ 076public class FileUtils { 077 078 /** 079 * Instances should NOT be constructed in standard programming. 080 */ 081 public FileUtils() { 082 super(); 083 } 084 085 /** 086 * The number of bytes in a kilobyte. 087 */ 088 public static final long ONE_KB = 1024; 089 090 /** 091 * The number of bytes in a kilobyte. 092 * 093 * @since 2.4 094 */ 095 public static final BigInteger ONE_KB_BI = BigInteger.valueOf(ONE_KB); 096 097 /** 098 * The number of bytes in a megabyte. 099 */ 100 public static final long ONE_MB = ONE_KB * ONE_KB; 101 102 /** 103 * The number of bytes in a megabyte. 104 * 105 * @since 2.4 106 */ 107 public static final BigInteger ONE_MB_BI = ONE_KB_BI.multiply(ONE_KB_BI); 108 109 /** 110 * The file copy buffer size (30 MB) 111 */ 112 private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30; 113 114 /** 115 * The number of bytes in a gigabyte. 116 */ 117 public static final long ONE_GB = ONE_KB * ONE_MB; 118 119 /** 120 * The number of bytes in a gigabyte. 121 * 122 * @since 2.4 123 */ 124 public static final BigInteger ONE_GB_BI = ONE_KB_BI.multiply(ONE_MB_BI); 125 126 /** 127 * The number of bytes in a terabyte. 128 */ 129 public static final long ONE_TB = ONE_KB * ONE_GB; 130 131 /** 132 * The number of bytes in a terabyte. 133 * 134 * @since 2.4 135 */ 136 public static final BigInteger ONE_TB_BI = ONE_KB_BI.multiply(ONE_GB_BI); 137 138 /** 139 * The number of bytes in a petabyte. 140 */ 141 public static final long ONE_PB = ONE_KB * ONE_TB; 142 143 /** 144 * The number of bytes in a petabyte. 145 * 146 * @since 2.4 147 */ 148 public static final BigInteger ONE_PB_BI = ONE_KB_BI.multiply(ONE_TB_BI); 149 150 /** 151 * The number of bytes in an exabyte. 152 */ 153 public static final long ONE_EB = ONE_KB * ONE_PB; 154 155 /** 156 * The number of bytes in an exabyte. 157 * 158 * @since 2.4 159 */ 160 public static final BigInteger ONE_EB_BI = ONE_KB_BI.multiply(ONE_PB_BI); 161 162 /** 163 * The number of bytes in a zettabyte. 164 */ 165 public static final BigInteger ONE_ZB = BigInteger.valueOf(ONE_KB).multiply(BigInteger.valueOf(ONE_EB)); 166 167 /** 168 * The number of bytes in a yottabyte. 169 */ 170 public static final BigInteger ONE_YB = ONE_KB_BI.multiply(ONE_ZB); 171 172 /** 173 * An empty array of type <code>File</code>. 174 */ 175 public static final File[] EMPTY_FILE_ARRAY = new File[0]; 176 177 /** 178 * The UTF-8 character set, used to decode octets in URLs. 179 */ 180 private static final Charset UTF8 = Charset.forName("UTF-8"); 181 182 //----------------------------------------------------------------------- 183 /** 184 * Construct a file from the set of name elements. 185 * 186 * @param directory the parent directory 187 * @param names the name elements 188 * @return the file 189 * @since 2.1 190 */ 191 public static File getFile(final File directory, final String... names) { 192 if (directory == null) { 193 throw new NullPointerException("directorydirectory must not be null"); 194 } 195 if (names == null) { 196 throw new NullPointerException("names must not be null"); 197 } 198 File file = directory; 199 for (final String name : names) { 200 file = new File(file, name); 201 } 202 return file; 203 } 204 205 /** 206 * Construct a file from the set of name elements. 207 * 208 * @param names the name elements 209 * @return the file 210 * @since 2.1 211 */ 212 public static File getFile(final String... names) { 213 if (names == null) { 214 throw new NullPointerException("names must not be null"); 215 } 216 File file = null; 217 for (final String name : names) { 218 if (file == null) { 219 file = new File(name); 220 } else { 221 file = new File(file, name); 222 } 223 } 224 return file; 225 } 226 227 /** 228 * Returns the path to the system temporary directory. 229 * 230 * @return the path to the system temporary directory. 231 * 232 * @since 2.0 233 */ 234 public static String getTempDirectoryPath() { 235 return System.getProperty("java.io.tmpdir"); 236 } 237 238 /** 239 * Returns a {@link File} representing the system temporary directory. 240 * 241 * @return the system temporary directory. 242 * 243 * @since 2.0 244 */ 245 public static File getTempDirectory() { 246 return new File(getTempDirectoryPath()); 247 } 248 249 /** 250 * Returns the path to the user's home directory. 251 * 252 * @return the path to the user's home directory. 253 * 254 * @since 2.0 255 */ 256 public static String getUserDirectoryPath() { 257 return System.getProperty("user.home"); 258 } 259 260 /** 261 * Returns a {@link File} representing the user's home directory. 262 * 263 * @return the user's home directory. 264 * 265 * @since 2.0 266 */ 267 public static File getUserDirectory() { 268 return new File(getUserDirectoryPath()); 269 } 270 271 //----------------------------------------------------------------------- 272 /** 273 * Opens a {@link FileInputStream} for the specified file, providing better 274 * error messages than simply calling <code>new FileInputStream(file)</code>. 275 * <p> 276 * At the end of the method either the stream will be successfully opened, 277 * or an exception will have been thrown. 278 * <p> 279 * An exception is thrown if the file does not exist. 280 * An exception is thrown if the file object exists but is a directory. 281 * An exception is thrown if the file exists but cannot be read. 282 * 283 * @param file the file to open for input, must not be {@code null} 284 * @return a new {@link FileInputStream} for the specified file 285 * @throws FileNotFoundException if the file does not exist 286 * @throws IOException if the file object is a directory 287 * @throws IOException if the file cannot be read 288 * @since 1.3 289 */ 290 public static FileInputStream openInputStream(final File file) throws IOException { 291 if (file.exists()) { 292 if (file.isDirectory()) { 293 throw new IOException("File '" + file + "' exists but is a directory"); 294 } 295 if (file.canRead() == false) { 296 throw new IOException("File '" + file + "' cannot be read"); 297 } 298 } else { 299 throw new FileNotFoundException("File '" + file + "' does not exist"); 300 } 301 return new FileInputStream(file); 302 } 303 304 //----------------------------------------------------------------------- 305 /** 306 * Opens a {@link FileOutputStream} for the specified file, checking and 307 * creating the parent directory if it does not exist. 308 * <p> 309 * At the end of the method either the stream will be successfully opened, 310 * or an exception will have been thrown. 311 * <p> 312 * The parent directory will be created if it does not exist. 313 * The file will be created if it does not exist. 314 * An exception is thrown if the file object exists but is a directory. 315 * An exception is thrown if the file exists but cannot be written to. 316 * An exception is thrown if the parent directory cannot be created. 317 * 318 * @param file the file to open for output, must not be {@code null} 319 * @return a new {@link FileOutputStream} for the specified file 320 * @throws IOException if the file object is a directory 321 * @throws IOException if the file cannot be written to 322 * @throws IOException if a parent directory needs creating but that fails 323 * @since 1.3 324 */ 325 public static FileOutputStream openOutputStream(final File file) throws IOException { 326 return openOutputStream(file, false); 327 } 328 329 /** 330 * Opens a {@link FileOutputStream} for the specified file, checking and 331 * creating the parent directory if it does not exist. 332 * <p> 333 * At the end of the method either the stream will be successfully opened, 334 * or an exception will have been thrown. 335 * <p> 336 * The parent directory will be created if it does not exist. 337 * The file will be created if it does not exist. 338 * An exception is thrown if the file object exists but is a directory. 339 * An exception is thrown if the file exists but cannot be written to. 340 * An exception is thrown if the parent directory cannot be created. 341 * 342 * @param file the file to open for output, must not be {@code null} 343 * @param append if {@code true}, then bytes will be added to the 344 * end of the file rather than overwriting 345 * @return a new {@link FileOutputStream} for the specified file 346 * @throws IOException if the file object is a directory 347 * @throws IOException if the file cannot be written to 348 * @throws IOException if a parent directory needs creating but that fails 349 * @since 2.1 350 */ 351 public static FileOutputStream openOutputStream(final File file, final boolean append) throws IOException { 352 if (file.exists()) { 353 if (file.isDirectory()) { 354 throw new IOException("File '" + file + "' exists but is a directory"); 355 } 356 if (file.canWrite() == false) { 357 throw new IOException("File '" + file + "' cannot be written to"); 358 } 359 } else { 360 final File parent = file.getParentFile(); 361 if (parent != null) { 362 if (!parent.mkdirs() && !parent.isDirectory()) { 363 throw new IOException("Directory '" + parent + "' could not be created"); 364 } 365 } 366 } 367 return new FileOutputStream(file, append); 368 } 369 370 //----------------------------------------------------------------------- 371 /** 372 * Returns a human-readable version of the file size, where the input represents a specific number of bytes. 373 * <p> 374 * If the size is over 1GB, the size is returned as the number of whole GB, i.e. the size is rounded down to the 375 * nearest GB boundary. 376 * </p> 377 * <p> 378 * Similarly for the 1MB and 1KB boundaries. 379 * </p> 380 * 381 * @param size 382 * the number of bytes 383 * @return a human-readable display value (includes units - EB, PB, TB, GB, MB, KB or bytes) 384 * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a> 385 * @since 2.4 386 */ 387 // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed? 388 public static String byteCountToDisplaySize(final BigInteger size) { 389 String displaySize; 390 391 if (size.divide(ONE_EB_BI).compareTo(BigInteger.ZERO) > 0) { 392 displaySize = String.valueOf(size.divide(ONE_EB_BI)) + " EB"; 393 } else if (size.divide(ONE_PB_BI).compareTo(BigInteger.ZERO) > 0) { 394 displaySize = String.valueOf(size.divide(ONE_PB_BI)) + " PB"; 395 } else if (size.divide(ONE_TB_BI).compareTo(BigInteger.ZERO) > 0) { 396 displaySize = String.valueOf(size.divide(ONE_TB_BI)) + " TB"; 397 } else if (size.divide(ONE_GB_BI).compareTo(BigInteger.ZERO) > 0) { 398 displaySize = String.valueOf(size.divide(ONE_GB_BI)) + " GB"; 399 } else if (size.divide(ONE_MB_BI).compareTo(BigInteger.ZERO) > 0) { 400 displaySize = String.valueOf(size.divide(ONE_MB_BI)) + " MB"; 401 } else if (size.divide(ONE_KB_BI).compareTo(BigInteger.ZERO) > 0) { 402 displaySize = String.valueOf(size.divide(ONE_KB_BI)) + " KB"; 403 } else { 404 displaySize = String.valueOf(size) + " bytes"; 405 } 406 return displaySize; 407 } 408 409 /** 410 * Returns a human-readable version of the file size, where the input represents a specific number of bytes. 411 * <p> 412 * If the size is over 1GB, the size is returned as the number of whole GB, i.e. the size is rounded down to the 413 * nearest GB boundary. 414 * </p> 415 * <p> 416 * Similarly for the 1MB and 1KB boundaries. 417 * </p> 418 * 419 * @param size 420 * the number of bytes 421 * @return a human-readable display value (includes units - EB, PB, TB, GB, MB, KB or bytes) 422 * @see <a href="https://issues.apache.org/jira/browse/IO-226">IO-226 - should the rounding be changed?</a> 423 */ 424 // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed? 425 public static String byteCountToDisplaySize(final long size) { 426 return byteCountToDisplaySize(BigInteger.valueOf(size)); 427 } 428 429 //----------------------------------------------------------------------- 430 /** 431 * Implements the same behaviour as the "touch" utility on Unix. It creates 432 * a new file with size 0 or, if the file exists already, it is opened and 433 * closed without modifying it, but updating the file date and time. 434 * <p> 435 * NOTE: As from v1.3, this method throws an IOException if the last 436 * modified date of the file cannot be set. Also, as from v1.3 this method 437 * creates parent directories if they do not exist. 438 * 439 * @param file the File to touch 440 * @throws IOException If an I/O problem occurs 441 */ 442 public static void touch(final File file) throws IOException { 443 if (!file.exists()) { 444 final OutputStream out = openOutputStream(file); 445 IOUtils.closeQuietly(out); 446 } 447 final boolean success = file.setLastModified(System.currentTimeMillis()); 448 if (!success) { 449 throw new IOException("Unable to set the last modification time for " + file); 450 } 451 } 452 453 //----------------------------------------------------------------------- 454 /** 455 * Converts a Collection containing java.io.File instanced into array 456 * representation. This is to account for the difference between 457 * File.listFiles() and FileUtils.listFiles(). 458 * 459 * @param files a Collection containing java.io.File instances 460 * @return an array of java.io.File 461 */ 462 public static File[] convertFileCollectionToFileArray(final Collection<File> files) { 463 return files.toArray(new File[files.size()]); 464 } 465 466 //----------------------------------------------------------------------- 467 /** 468 * Finds files within a given directory (and optionally its 469 * subdirectories). All files found are filtered by an IOFileFilter. 470 * 471 * @param files the collection of files found. 472 * @param directory the directory to search in. 473 * @param filter the filter to apply to files and directories. 474 * @param includeSubDirectories indicates if will include the subdirectories themselves 475 */ 476 private static void innerListFiles(final Collection<File> files, final File directory, 477 final IOFileFilter filter, final boolean includeSubDirectories) { 478 final File[] found = directory.listFiles((FileFilter) filter); 479 480 if (found != null) { 481 for (final File file : found) { 482 if (file.isDirectory()) { 483 if (includeSubDirectories) { 484 files.add(file); 485 } 486 innerListFiles(files, file, filter, includeSubDirectories); 487 } else { 488 files.add(file); 489 } 490 } 491 } 492 } 493 494 /** 495 * Finds files within a given directory (and optionally its 496 * subdirectories). All files found are filtered by an IOFileFilter. 497 * <p> 498 * If your search should recurse into subdirectories you can pass in 499 * an IOFileFilter for directories. You don't need to bind a 500 * DirectoryFileFilter (via logical AND) to this filter. This method does 501 * that for you. 502 * <p> 503 * An example: If you want to search through all directories called 504 * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code> 505 * <p> 506 * Another common usage of this method is find files in a directory 507 * tree but ignoring the directories generated CVS. You can simply pass 508 * in <code>FileFilterUtils.makeCVSAware(null)</code>. 509 * 510 * @param directory the directory to search in 511 * @param fileFilter filter to apply when finding files. 512 * @param dirFilter optional filter to apply when finding subdirectories. 513 * If this parameter is {@code null}, subdirectories will not be included in the 514 * search. Use TrueFileFilter.INSTANCE to match all directories. 515 * @return an collection of java.io.File with the matching files 516 * @see org.apache.commons.io.filefilter.FileFilterUtils 517 * @see org.apache.commons.io.filefilter.NameFileFilter 518 */ 519 public static Collection<File> listFiles( 520 final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) { 521 validateListFilesParameters(directory, fileFilter); 522 523 final IOFileFilter effFileFilter = setUpEffectiveFileFilter(fileFilter); 524 final IOFileFilter effDirFilter = setUpEffectiveDirFilter(dirFilter); 525 526 //Find files 527 final Collection<File> files = new java.util.LinkedList<File>(); 528 innerListFiles(files, directory, 529 FileFilterUtils.or(effFileFilter, effDirFilter), false); 530 return files; 531 } 532 533 /** 534 * Validates the given arguments. 535 * <ul> 536 * <li>Throws {@link IllegalArgumentException} if {@code directory} is not a directory</li> 537 * <li>Throws {@link NullPointerException} if {@code fileFilter} is null</li> 538 * </ul> 539 * 540 * @param directory The File to test 541 * @param fileFilter The IOFileFilter to test 542 */ 543 private static void validateListFilesParameters(final File directory, final IOFileFilter fileFilter) { 544 if (!directory.isDirectory()) { 545 throw new IllegalArgumentException("Parameter 'directory' is not a directory: " + directory); 546 } 547 if (fileFilter == null) { 548 throw new NullPointerException("Parameter 'fileFilter' is null"); 549 } 550 } 551 552 /** 553 * Returns a filter that accepts files in addition to the {@link File} objects accepted by the given filter. 554 * 555 * @param fileFilter a base filter to add to 556 * @return a filter that accepts files 557 */ 558 private static IOFileFilter setUpEffectiveFileFilter(final IOFileFilter fileFilter) { 559 return FileFilterUtils.and(fileFilter, FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE)); 560 } 561 562 /** 563 * Returns a filter that accepts directories in addition to the {@link File} objects accepted by the given filter. 564 * 565 * @param dirFilter a base filter to add to 566 * @return a filter that accepts directories 567 */ 568 private static IOFileFilter setUpEffectiveDirFilter(final IOFileFilter dirFilter) { 569 return dirFilter == null ? FalseFileFilter.INSTANCE : FileFilterUtils.and(dirFilter, 570 DirectoryFileFilter.INSTANCE); 571 } 572 573 /** 574 * Finds files within a given directory (and optionally its 575 * subdirectories). All files found are filtered by an IOFileFilter. 576 * <p> 577 * The resulting collection includes the starting directory and 578 * any subdirectories that match the directory filter. 579 * <p> 580 * @see org.apache.commons.io.FileUtils#listFiles 581 * 582 * @param directory the directory to search in 583 * @param fileFilter filter to apply when finding files. 584 * @param dirFilter optional filter to apply when finding subdirectories. 585 * If this parameter is {@code null}, subdirectories will not be included in the 586 * search. Use TrueFileFilter.INSTANCE to match all directories. 587 * @return an collection of java.io.File with the matching files 588 * @see org.apache.commons.io.filefilter.FileFilterUtils 589 * @see org.apache.commons.io.filefilter.NameFileFilter 590 * @since 2.2 591 */ 592 public static Collection<File> listFilesAndDirs( 593 final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) { 594 validateListFilesParameters(directory, fileFilter); 595 596 final IOFileFilter effFileFilter = setUpEffectiveFileFilter(fileFilter); 597 final IOFileFilter effDirFilter = setUpEffectiveDirFilter(dirFilter); 598 599 //Find files 600 final Collection<File> files = new java.util.LinkedList<File>(); 601 if (directory.isDirectory()) { 602 files.add(directory); 603 } 604 innerListFiles(files, directory, 605 FileFilterUtils.or(effFileFilter, effDirFilter), true); 606 return files; 607 } 608 609 /** 610 * Allows iteration over the files in given directory (and optionally 611 * its subdirectories). 612 * <p> 613 * All files found are filtered by an IOFileFilter. This method is 614 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}, 615 * which supports Iterable ('foreach' loop). 616 * <p> 617 * @param directory the directory to search in 618 * @param fileFilter filter to apply when finding files. 619 * @param dirFilter optional filter to apply when finding subdirectories. 620 * If this parameter is {@code null}, subdirectories will not be included in the 621 * search. Use TrueFileFilter.INSTANCE to match all directories. 622 * @return an iterator of java.io.File for the matching files 623 * @see org.apache.commons.io.filefilter.FileFilterUtils 624 * @see org.apache.commons.io.filefilter.NameFileFilter 625 * @since 1.2 626 */ 627 public static Iterator<File> iterateFiles( 628 final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) { 629 return listFiles(directory, fileFilter, dirFilter).iterator(); 630 } 631 632 /** 633 * Allows iteration over the files in given directory (and optionally 634 * its subdirectories). 635 * <p> 636 * All files found are filtered by an IOFileFilter. This method is 637 * based on {@link #listFilesAndDirs(File, IOFileFilter, IOFileFilter)}, 638 * which supports Iterable ('foreach' loop). 639 * <p> 640 * The resulting iterator includes the subdirectories themselves. 641 * 642 * @param directory the directory to search in 643 * @param fileFilter filter to apply when finding files. 644 * @param dirFilter optional filter to apply when finding subdirectories. 645 * If this parameter is {@code null}, subdirectories will not be included in the 646 * search. Use TrueFileFilter.INSTANCE to match all directories. 647 * @return an iterator of java.io.File for the matching files 648 * @see org.apache.commons.io.filefilter.FileFilterUtils 649 * @see org.apache.commons.io.filefilter.NameFileFilter 650 * @since 2.2 651 */ 652 public static Iterator<File> iterateFilesAndDirs(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter) { 653 return listFilesAndDirs(directory, fileFilter, dirFilter).iterator(); 654 } 655 656 //----------------------------------------------------------------------- 657 /** 658 * Converts an array of file extensions to suffixes for use 659 * with IOFileFilters. 660 * 661 * @param extensions an array of extensions. Format: {"java", "xml"} 662 * @return an array of suffixes. Format: {".java", ".xml"} 663 */ 664 private static String[] toSuffixes(final String[] extensions) { 665 final String[] suffixes = new String[extensions.length]; 666 for (int i = 0; i < extensions.length; i++) { 667 suffixes[i] = "." + extensions[i]; 668 } 669 return suffixes; 670 } 671 672 673 /** 674 * Finds files within a given directory (and optionally its subdirectories) 675 * which match an array of extensions. 676 * 677 * @param directory the directory to search in 678 * @param extensions an array of extensions, ex. {"java","xml"}. If this 679 * parameter is {@code null}, all files are returned. 680 * @param recursive if true all subdirectories are searched as well 681 * @return an collection of java.io.File with the matching files 682 */ 683 public static Collection<File> listFiles( 684 final File directory, final String[] extensions, final boolean recursive) { 685 IOFileFilter filter; 686 if (extensions == null) { 687 filter = TrueFileFilter.INSTANCE; 688 } else { 689 final String[] suffixes = toSuffixes(extensions); 690 filter = new SuffixFileFilter(suffixes); 691 } 692 return listFiles(directory, filter, 693 recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE); 694 } 695 696 /** 697 * Allows iteration over the files in a given directory (and optionally 698 * its subdirectories) which match an array of extensions. This method 699 * is based on {@link #listFiles(File, String[], boolean)}, 700 * which supports Iterable ('foreach' loop). 701 * 702 * @param directory the directory to search in 703 * @param extensions an array of extensions, ex. {"java","xml"}. If this 704 * parameter is {@code null}, all files are returned. 705 * @param recursive if true all subdirectories are searched as well 706 * @return an iterator of java.io.File with the matching files 707 * @since 1.2 708 */ 709 public static Iterator<File> iterateFiles( 710 final File directory, final String[] extensions, final boolean recursive) { 711 return listFiles(directory, extensions, recursive).iterator(); 712 } 713 714 //----------------------------------------------------------------------- 715 /** 716 * Compares the contents of two files to determine if they are equal or not. 717 * <p> 718 * This method checks to see if the two files are different lengths 719 * or if they point to the same file, before resorting to byte-by-byte 720 * comparison of the contents. 721 * <p> 722 * Code origin: Avalon 723 * 724 * @param file1 the first file 725 * @param file2 the second file 726 * @return true if the content of the files are equal or they both don't 727 * exist, false otherwise 728 * @throws IOException in case of an I/O error 729 */ 730 public static boolean contentEquals(final File file1, final File file2) throws IOException { 731 final boolean file1Exists = file1.exists(); 732 if (file1Exists != file2.exists()) { 733 return false; 734 } 735 736 if (!file1Exists) { 737 // two not existing files are equal 738 return true; 739 } 740 741 if (file1.isDirectory() || file2.isDirectory()) { 742 // don't want to compare directory contents 743 throw new IOException("Can't compare directories, only files"); 744 } 745 746 if (file1.length() != file2.length()) { 747 // lengths differ, cannot be equal 748 return false; 749 } 750 751 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 752 // same file 753 return true; 754 } 755 756 InputStream input1 = null; 757 InputStream input2 = null; 758 try { 759 input1 = new FileInputStream(file1); 760 input2 = new FileInputStream(file2); 761 return IOUtils.contentEquals(input1, input2); 762 763 } finally { 764 IOUtils.closeQuietly(input1); 765 IOUtils.closeQuietly(input2); 766 } 767 } 768 769 //----------------------------------------------------------------------- 770 /** 771 * Compares the contents of two files to determine if they are equal or not. 772 * <p> 773 * This method checks to see if the two files point to the same file, 774 * before resorting to line-by-line comparison of the contents. 775 * <p> 776 * 777 * @param file1 the first file 778 * @param file2 the second file 779 * @param charsetName the character encoding to be used. 780 * May be null, in which case the platform default is used 781 * @return true if the content of the files are equal or neither exists, 782 * false otherwise 783 * @throws IOException in case of an I/O error 784 * @since 2.2 785 * @see IOUtils#contentEqualsIgnoreEOL(Reader, Reader) 786 */ 787 public static boolean contentEqualsIgnoreEOL(final File file1, final File file2, final String charsetName) throws IOException { 788 final boolean file1Exists = file1.exists(); 789 if (file1Exists != file2.exists()) { 790 return false; 791 } 792 793 if (!file1Exists) { 794 // two not existing files are equal 795 return true; 796 } 797 798 if (file1.isDirectory() || file2.isDirectory()) { 799 // don't want to compare directory contents 800 throw new IOException("Can't compare directories, only files"); 801 } 802 803 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 804 // same file 805 return true; 806 } 807 808 Reader input1 = null; 809 Reader input2 = null; 810 try { 811 if (charsetName == null) { 812 // N.B. make explicit the use of the default charset 813 input1 = new InputStreamReader(new FileInputStream(file1), Charset.defaultCharset()); 814 input2 = new InputStreamReader(new FileInputStream(file2), Charset.defaultCharset()); 815 } else { 816 input1 = new InputStreamReader(new FileInputStream(file1), charsetName); 817 input2 = new InputStreamReader(new FileInputStream(file2), charsetName); 818 } 819 return IOUtils.contentEqualsIgnoreEOL(input1, input2); 820 821 } finally { 822 IOUtils.closeQuietly(input1); 823 IOUtils.closeQuietly(input2); 824 } 825 } 826 827 //----------------------------------------------------------------------- 828 /** 829 * Convert from a <code>URL</code> to a <code>File</code>. 830 * <p> 831 * From version 1.1 this method will decode the URL. 832 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 833 * correctly decoded to <code>/my docs/file.txt</code>. Starting with version 834 * 1.5, this method uses UTF-8 to decode percent-encoded octets to characters. 835 * Additionally, malformed percent-encoded octets are handled leniently by 836 * passing them through literally. 837 * 838 * @param url the file URL to convert, {@code null} returns {@code null} 839 * @return the equivalent <code>File</code> object, or {@code null} 840 * if the URL's protocol is not <code>file</code> 841 */ 842 public static File toFile(final URL url) { 843 if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) { 844 return null; 845 } else { 846 String filename = url.getFile().replace('/', File.separatorChar); 847 filename = decodeUrl(filename); 848 return new File(filename); 849 } 850 } 851 852 /** 853 * Decodes the specified URL as per RFC 3986, i.e. transforms 854 * percent-encoded octets to characters by decoding with the UTF-8 character 855 * set. This function is primarily intended for usage with 856 * {@link java.net.URL} which unfortunately does not enforce proper URLs. As 857 * such, this method will leniently accept invalid characters or malformed 858 * percent-encoded octets and simply pass them literally through to the 859 * result string. Except for rare edge cases, this will make unencoded URLs 860 * pass through unaltered. 861 * 862 * @param url The URL to decode, may be {@code null}. 863 * @return The decoded URL or {@code null} if the input was 864 * {@code null}. 865 */ 866 static String decodeUrl(final String url) { 867 String decoded = url; 868 if (url != null && url.indexOf('%') >= 0) { 869 final int n = url.length(); 870 final StringBuffer buffer = new StringBuffer(); 871 final ByteBuffer bytes = ByteBuffer.allocate(n); 872 for (int i = 0; i < n;) { 873 if (url.charAt(i) == '%') { 874 try { 875 do { 876 final byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16); 877 bytes.put(octet); 878 i += 3; 879 } while (i < n && url.charAt(i) == '%'); 880 continue; 881 } catch (final RuntimeException e) { 882 // malformed percent-encoded octet, fall through and 883 // append characters literally 884 } finally { 885 if (bytes.position() > 0) { 886 bytes.flip(); 887 buffer.append(UTF8.decode(bytes).toString()); 888 bytes.clear(); 889 } 890 } 891 } 892 buffer.append(url.charAt(i++)); 893 } 894 decoded = buffer.toString(); 895 } 896 return decoded; 897 } 898 899 /** 900 * Converts each of an array of <code>URL</code> to a <code>File</code>. 901 * <p> 902 * Returns an array of the same size as the input. 903 * If the input is {@code null}, an empty array is returned. 904 * If the input contains {@code null}, the output array contains {@code null} at the same 905 * index. 906 * <p> 907 * This method will decode the URL. 908 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 909 * correctly decoded to <code>/my docs/file.txt</code>. 910 * 911 * @param urls the file URLs to convert, {@code null} returns empty array 912 * @return a non-{@code null} array of Files matching the input, with a {@code null} item 913 * if there was a {@code null} at that index in the input array 914 * @throws IllegalArgumentException if any file is not a URL file 915 * @throws IllegalArgumentException if any file is incorrectly encoded 916 * @since 1.1 917 */ 918 public static File[] toFiles(final URL[] urls) { 919 if (urls == null || urls.length == 0) { 920 return EMPTY_FILE_ARRAY; 921 } 922 final File[] files = new File[urls.length]; 923 for (int i = 0; i < urls.length; i++) { 924 final URL url = urls[i]; 925 if (url != null) { 926 if (url.getProtocol().equals("file") == false) { 927 throw new IllegalArgumentException( 928 "URL could not be converted to a File: " + url); 929 } 930 files[i] = toFile(url); 931 } 932 } 933 return files; 934 } 935 936 /** 937 * Converts each of an array of <code>File</code> to a <code>URL</code>. 938 * <p> 939 * Returns an array of the same size as the input. 940 * 941 * @param files the files to convert, must not be {@code null} 942 * @return an array of URLs matching the input 943 * @throws IOException if a file cannot be converted 944 * @throws NullPointerException if the parameter is null 945 */ 946 public static URL[] toURLs(final File[] files) throws IOException { 947 final URL[] urls = new URL[files.length]; 948 949 for (int i = 0; i < urls.length; i++) { 950 urls[i] = files[i].toURI().toURL(); 951 } 952 953 return urls; 954 } 955 956 //----------------------------------------------------------------------- 957 /** 958 * Copies a file to a directory preserving the file date. 959 * <p> 960 * This method copies the contents of the specified source file 961 * to a file of the same name in the specified destination directory. 962 * The destination directory is created if it does not exist. 963 * If the destination file exists, then this method will overwrite it. 964 * <p> 965 * <strong>Note:</strong> This method tries to preserve the file's last 966 * modified date/times using {@link File#setLastModified(long)}, however 967 * it is not guaranteed that the operation will succeed. 968 * If the modification operation fails, no indication is provided. 969 * 970 * @param srcFile an existing file to copy, must not be {@code null} 971 * @param destDir the directory to place the copy in, must not be {@code null} 972 * 973 * @throws NullPointerException if source or destination is null 974 * @throws IOException if source or destination is invalid 975 * @throws IOException if an IO error occurs during copying 976 * @see #copyFile(File, File, boolean) 977 */ 978 public static void copyFileToDirectory(final File srcFile, final File destDir) throws IOException { 979 copyFileToDirectory(srcFile, destDir, true); 980 } 981 982 /** 983 * Copies a file to a directory optionally preserving the file date. 984 * <p> 985 * This method copies the contents of the specified source file 986 * to a file of the same name in the specified destination directory. 987 * The destination directory is created if it does not exist. 988 * If the destination file exists, then this method will overwrite it. 989 * <p> 990 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 991 * {@code true} tries to preserve the file's last modified 992 * date/times using {@link File#setLastModified(long)}, however it is 993 * not guaranteed that the operation will succeed. 994 * If the modification operation fails, no indication is provided. 995 * 996 * @param srcFile an existing file to copy, must not be {@code null} 997 * @param destDir the directory to place the copy in, must not be {@code null} 998 * @param preserveFileDate true if the file date of the copy 999 * should be the same as the original 1000 * 1001 * @throws NullPointerException if source or destination is {@code null} 1002 * @throws IOException if source or destination is invalid 1003 * @throws IOException if an IO error occurs during copying 1004 * @see #copyFile(File, File, boolean) 1005 * @since 1.3 1006 */ 1007 public static void copyFileToDirectory(final File srcFile, final File destDir, final boolean preserveFileDate) throws IOException { 1008 if (destDir == null) { 1009 throw new NullPointerException("Destination must not be null"); 1010 } 1011 if (destDir.exists() && destDir.isDirectory() == false) { 1012 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 1013 } 1014 final File destFile = new File(destDir, srcFile.getName()); 1015 copyFile(srcFile, destFile, preserveFileDate); 1016 } 1017 1018 /** 1019 * Copies a file to a new location preserving the file date. 1020 * <p> 1021 * This method copies the contents of the specified source file to the 1022 * specified destination file. The directory holding the destination file is 1023 * created if it does not exist. If the destination file exists, then this 1024 * method will overwrite it. 1025 * <p> 1026 * <strong>Note:</strong> This method tries to preserve the file's last 1027 * modified date/times using {@link File#setLastModified(long)}, however 1028 * it is not guaranteed that the operation will succeed. 1029 * If the modification operation fails, no indication is provided. 1030 * 1031 * @param srcFile an existing file to copy, must not be {@code null} 1032 * @param destFile the new file, must not be {@code null} 1033 * 1034 * @throws NullPointerException if source or destination is {@code null} 1035 * @throws IOException if source or destination is invalid 1036 * @throws IOException if an IO error occurs during copying 1037 * @see #copyFileToDirectory(File, File) 1038 */ 1039 public static void copyFile(final File srcFile, final File destFile) throws IOException { 1040 copyFile(srcFile, destFile, true); 1041 } 1042 1043 /** 1044 * Copies a file to a new location. 1045 * <p> 1046 * This method copies the contents of the specified source file 1047 * to the specified destination file. 1048 * The directory holding the destination file is created if it does not exist. 1049 * If the destination file exists, then this method will overwrite it. 1050 * <p> 1051 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1052 * {@code true} tries to preserve the file's last modified 1053 * date/times using {@link File#setLastModified(long)}, however it is 1054 * not guaranteed that the operation will succeed. 1055 * If the modification operation fails, no indication is provided. 1056 * 1057 * @param srcFile an existing file to copy, must not be {@code null} 1058 * @param destFile the new file, must not be {@code null} 1059 * @param preserveFileDate true if the file date of the copy 1060 * should be the same as the original 1061 * 1062 * @throws NullPointerException if source or destination is {@code null} 1063 * @throws IOException if source or destination is invalid 1064 * @throws IOException if an IO error occurs during copying 1065 * @see #copyFileToDirectory(File, File, boolean) 1066 */ 1067 public static void copyFile(final File srcFile, final File destFile, 1068 final boolean preserveFileDate) throws IOException { 1069 if (srcFile == null) { 1070 throw new NullPointerException("Source must not be null"); 1071 } 1072 if (destFile == null) { 1073 throw new NullPointerException("Destination must not be null"); 1074 } 1075 if (srcFile.exists() == false) { 1076 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 1077 } 1078 if (srcFile.isDirectory()) { 1079 throw new IOException("Source '" + srcFile + "' exists but is a directory"); 1080 } 1081 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { 1082 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); 1083 } 1084 final File parentFile = destFile.getParentFile(); 1085 if (parentFile != null) { 1086 if (!parentFile.mkdirs() && !parentFile.isDirectory()) { 1087 throw new IOException("Destination '" + parentFile + "' directory cannot be created"); 1088 } 1089 } 1090 if (destFile.exists() && destFile.canWrite() == false) { 1091 throw new IOException("Destination '" + destFile + "' exists but is read-only"); 1092 } 1093 doCopyFile(srcFile, destFile, preserveFileDate); 1094 } 1095 1096 /** 1097 * Copy bytes from a <code>File</code> to an <code>OutputStream</code>. 1098 * <p> 1099 * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>. 1100 * </p> 1101 * 1102 * @param input 1103 * the <code>File</code> to read from 1104 * @param output 1105 * the <code>OutputStream</code> to write to 1106 * @return the number of bytes copied 1107 * @throws NullPointerException 1108 * if the input or output is null 1109 * @throws IOException 1110 * if an I/O error occurs 1111 * @since 2.1 1112 */ 1113 public static long copyFile(final File input, final OutputStream output) throws IOException { 1114 final FileInputStream fis = new FileInputStream(input); 1115 try { 1116 return IOUtils.copyLarge(fis, output); 1117 } finally { 1118 fis.close(); 1119 } 1120 } 1121 1122 /** 1123 * Internal copy file method. 1124 * 1125 * @param srcFile the validated source file, must not be {@code null} 1126 * @param destFile the validated destination file, must not be {@code null} 1127 * @param preserveFileDate whether to preserve the file date 1128 * @throws IOException if an error occurs 1129 */ 1130 private static void doCopyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException { 1131 if (destFile.exists() && destFile.isDirectory()) { 1132 throw new IOException("Destination '" + destFile + "' exists but is a directory"); 1133 } 1134 1135 FileInputStream fis = null; 1136 FileOutputStream fos = null; 1137 FileChannel input = null; 1138 FileChannel output = null; 1139 try { 1140 fis = new FileInputStream(srcFile); 1141 fos = new FileOutputStream(destFile); 1142 input = fis.getChannel(); 1143 output = fos.getChannel(); 1144 final long size = input.size(); 1145 long pos = 0; 1146 long count = 0; 1147 while (pos < size) { 1148 count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; 1149 pos += output.transferFrom(input, pos, count); 1150 } 1151 } finally { 1152 IOUtils.closeQuietly(output); 1153 IOUtils.closeQuietly(fos); 1154 IOUtils.closeQuietly(input); 1155 IOUtils.closeQuietly(fis); 1156 } 1157 1158 if (srcFile.length() != destFile.length()) { 1159 throw new IOException("Failed to copy full contents from '" + 1160 srcFile + "' to '" + destFile + "'"); 1161 } 1162 if (preserveFileDate) { 1163 destFile.setLastModified(srcFile.lastModified()); 1164 } 1165 } 1166 1167 //----------------------------------------------------------------------- 1168 /** 1169 * Copies a directory to within another directory preserving the file dates. 1170 * <p> 1171 * This method copies the source directory and all its contents to a 1172 * directory of the same name in the specified destination directory. 1173 * <p> 1174 * The destination directory is created if it does not exist. 1175 * If the destination directory did exist, then this method merges 1176 * the source with the destination, with the source taking precedence. 1177 * <p> 1178 * <strong>Note:</strong> This method tries to preserve the files' last 1179 * modified date/times using {@link File#setLastModified(long)}, however 1180 * it is not guaranteed that those operations will succeed. 1181 * If the modification operation fails, no indication is provided. 1182 * 1183 * @param srcDir an existing directory to copy, must not be {@code null} 1184 * @param destDir the directory to place the copy in, must not be {@code null} 1185 * 1186 * @throws NullPointerException if source or destination is {@code null} 1187 * @throws IOException if source or destination is invalid 1188 * @throws IOException if an IO error occurs during copying 1189 * @since 1.2 1190 */ 1191 public static void copyDirectoryToDirectory(final File srcDir, final File destDir) throws IOException { 1192 if (srcDir == null) { 1193 throw new NullPointerException("Source must not be null"); 1194 } 1195 if (srcDir.exists() && srcDir.isDirectory() == false) { 1196 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory"); 1197 } 1198 if (destDir == null) { 1199 throw new NullPointerException("Destination must not be null"); 1200 } 1201 if (destDir.exists() && destDir.isDirectory() == false) { 1202 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 1203 } 1204 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true); 1205 } 1206 1207 /** 1208 * Copies a whole directory to a new location preserving the file dates. 1209 * <p> 1210 * This method copies the specified directory and all its child 1211 * directories and files to the specified destination. 1212 * The destination is the new location and name of the directory. 1213 * <p> 1214 * The destination directory is created if it does not exist. 1215 * If the destination directory did exist, then this method merges 1216 * the source with the destination, with the source taking precedence. 1217 * <p> 1218 * <strong>Note:</strong> This method tries to preserve the files' last 1219 * modified date/times using {@link File#setLastModified(long)}, however 1220 * it is not guaranteed that those operations will succeed. 1221 * If the modification operation fails, no indication is provided. 1222 * 1223 * @param srcDir an existing directory to copy, must not be {@code null} 1224 * @param destDir the new directory, must not be {@code null} 1225 * 1226 * @throws NullPointerException if source or destination is {@code null} 1227 * @throws IOException if source or destination is invalid 1228 * @throws IOException if an IO error occurs during copying 1229 * @since 1.1 1230 */ 1231 public static void copyDirectory(final File srcDir, final File destDir) throws IOException { 1232 copyDirectory(srcDir, destDir, true); 1233 } 1234 1235 /** 1236 * Copies a whole directory to a new location. 1237 * <p> 1238 * This method copies the contents of the specified source directory 1239 * to within the specified destination directory. 1240 * <p> 1241 * The destination directory is created if it does not exist. 1242 * If the destination directory did exist, then this method merges 1243 * the source with the destination, with the source taking precedence. 1244 * <p> 1245 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1246 * {@code true} tries to preserve the files' last modified 1247 * date/times using {@link File#setLastModified(long)}, however it is 1248 * not guaranteed that those operations will succeed. 1249 * If the modification operation fails, no indication is provided. 1250 * 1251 * @param srcDir an existing directory to copy, must not be {@code null} 1252 * @param destDir the new directory, must not be {@code null} 1253 * @param preserveFileDate true if the file date of the copy 1254 * should be the same as the original 1255 * 1256 * @throws NullPointerException if source or destination is {@code null} 1257 * @throws IOException if source or destination is invalid 1258 * @throws IOException if an IO error occurs during copying 1259 * @since 1.1 1260 */ 1261 public static void copyDirectory(final File srcDir, final File destDir, 1262 final boolean preserveFileDate) throws IOException { 1263 copyDirectory(srcDir, destDir, null, preserveFileDate); 1264 } 1265 1266 /** 1267 * Copies a filtered directory to a new location preserving the file dates. 1268 * <p> 1269 * This method copies the contents of the specified source directory 1270 * to within the specified destination directory. 1271 * <p> 1272 * The destination directory is created if it does not exist. 1273 * If the destination directory did exist, then this method merges 1274 * the source with the destination, with the source taking precedence. 1275 * <p> 1276 * <strong>Note:</strong> This method tries to preserve the files' last 1277 * modified date/times using {@link File#setLastModified(long)}, however 1278 * it is not guaranteed that those operations will succeed. 1279 * If the modification operation fails, no indication is provided. 1280 * 1281 * <h4>Example: Copy directories only</h4> 1282 * <pre> 1283 * // only copy the directory structure 1284 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY); 1285 * </pre> 1286 * 1287 * <h4>Example: Copy directories and txt files</h4> 1288 * <pre> 1289 * // Create a filter for ".txt" files 1290 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 1291 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 1292 * 1293 * // Create a filter for either directories or ".txt" files 1294 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 1295 * 1296 * // Copy using the filter 1297 * FileUtils.copyDirectory(srcDir, destDir, filter); 1298 * </pre> 1299 * 1300 * @param srcDir an existing directory to copy, must not be {@code null} 1301 * @param destDir the new directory, must not be {@code null} 1302 * @param filter the filter to apply, null means copy all directories and files 1303 * should be the same as the original 1304 * 1305 * @throws NullPointerException if source or destination is {@code null} 1306 * @throws IOException if source or destination is invalid 1307 * @throws IOException if an IO error occurs during copying 1308 * @since 1.4 1309 */ 1310 public static void copyDirectory(final File srcDir, final File destDir, 1311 final FileFilter filter) throws IOException { 1312 copyDirectory(srcDir, destDir, filter, true); 1313 } 1314 1315 /** 1316 * Copies a filtered directory to a new location. 1317 * <p> 1318 * This method copies the contents of the specified source directory 1319 * to within the specified destination directory. 1320 * <p> 1321 * The destination directory is created if it does not exist. 1322 * If the destination directory did exist, then this method merges 1323 * the source with the destination, with the source taking precedence. 1324 * <p> 1325 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1326 * {@code true} tries to preserve the files' last modified 1327 * date/times using {@link File#setLastModified(long)}, however it is 1328 * not guaranteed that those operations will succeed. 1329 * If the modification operation fails, no indication is provided. 1330 * 1331 * <h4>Example: Copy directories only</h4> 1332 * <pre> 1333 * // only copy the directory structure 1334 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false); 1335 * </pre> 1336 * 1337 * <h4>Example: Copy directories and txt files</h4> 1338 * <pre> 1339 * // Create a filter for ".txt" files 1340 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 1341 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 1342 * 1343 * // Create a filter for either directories or ".txt" files 1344 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 1345 * 1346 * // Copy using the filter 1347 * FileUtils.copyDirectory(srcDir, destDir, filter, false); 1348 * </pre> 1349 * 1350 * @param srcDir an existing directory to copy, must not be {@code null} 1351 * @param destDir the new directory, must not be {@code null} 1352 * @param filter the filter to apply, null means copy all directories and files 1353 * @param preserveFileDate true if the file date of the copy 1354 * should be the same as the original 1355 * 1356 * @throws NullPointerException if source or destination is {@code null} 1357 * @throws IOException if source or destination is invalid 1358 * @throws IOException if an IO error occurs during copying 1359 * @since 1.4 1360 */ 1361 public static void copyDirectory(final File srcDir, final File destDir, 1362 final FileFilter filter, final boolean preserveFileDate) throws IOException { 1363 if (srcDir == null) { 1364 throw new NullPointerException("Source must not be null"); 1365 } 1366 if (destDir == null) { 1367 throw new NullPointerException("Destination must not be null"); 1368 } 1369 if (srcDir.exists() == false) { 1370 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 1371 } 1372 if (srcDir.isDirectory() == false) { 1373 throw new IOException("Source '" + srcDir + "' exists but is not a directory"); 1374 } 1375 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) { 1376 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); 1377 } 1378 1379 // Cater for destination being directory within the source directory (see IO-141) 1380 List<String> exclusionList = null; 1381 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) { 1382 final File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 1383 if (srcFiles != null && srcFiles.length > 0) { 1384 exclusionList = new ArrayList<String>(srcFiles.length); 1385 for (final File srcFile : srcFiles) { 1386 final File copiedFile = new File(destDir, srcFile.getName()); 1387 exclusionList.add(copiedFile.getCanonicalPath()); 1388 } 1389 } 1390 } 1391 doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList); 1392 } 1393 1394 /** 1395 * Internal copy directory method. 1396 * 1397 * @param srcDir the validated source directory, must not be {@code null} 1398 * @param destDir the validated destination directory, must not be {@code null} 1399 * @param filter the filter to apply, null means copy all directories and files 1400 * @param preserveFileDate whether to preserve the file date 1401 * @param exclusionList List of files and directories to exclude from the copy, may be null 1402 * @throws IOException if an error occurs 1403 * @since 1.1 1404 */ 1405 private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter filter, 1406 final boolean preserveFileDate, final List<String> exclusionList) throws IOException { 1407 // recurse 1408 final File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 1409 if (srcFiles == null) { // null if abstract pathname does not denote a directory, or if an I/O error occurs 1410 throw new IOException("Failed to list contents of " + srcDir); 1411 } 1412 if (destDir.exists()) { 1413 if (destDir.isDirectory() == false) { 1414 throw new IOException("Destination '" + destDir + "' exists but is not a directory"); 1415 } 1416 } else { 1417 if (!destDir.mkdirs() && !destDir.isDirectory()) { 1418 throw new IOException("Destination '" + destDir + "' directory cannot be created"); 1419 } 1420 } 1421 if (destDir.canWrite() == false) { 1422 throw new IOException("Destination '" + destDir + "' cannot be written to"); 1423 } 1424 for (final File srcFile : srcFiles) { 1425 final File dstFile = new File(destDir, srcFile.getName()); 1426 if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) { 1427 if (srcFile.isDirectory()) { 1428 doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList); 1429 } else { 1430 doCopyFile(srcFile, dstFile, preserveFileDate); 1431 } 1432 } 1433 } 1434 1435 // Do this last, as the above has probably affected directory metadata 1436 if (preserveFileDate) { 1437 destDir.setLastModified(srcDir.lastModified()); 1438 } 1439 } 1440 1441 //----------------------------------------------------------------------- 1442 /** 1443 * Copies bytes from the URL <code>source</code> to a file 1444 * <code>destination</code>. The directories up to <code>destination</code> 1445 * will be created if they don't already exist. <code>destination</code> 1446 * will be overwritten if it already exists. 1447 * <p> 1448 * Warning: this method does not set a connection or read timeout and thus 1449 * might block forever. Use {@link #copyURLToFile(URL, File, int, int)} 1450 * with reasonable timeouts to prevent this. 1451 * 1452 * @param source the <code>URL</code> to copy bytes from, must not be {@code null} 1453 * @param destination the non-directory <code>File</code> to write bytes to 1454 * (possibly overwriting), must not be {@code null} 1455 * @throws IOException if <code>source</code> URL cannot be opened 1456 * @throws IOException if <code>destination</code> is a directory 1457 * @throws IOException if <code>destination</code> cannot be written 1458 * @throws IOException if <code>destination</code> needs creating but can't be 1459 * @throws IOException if an IO error occurs during copying 1460 */ 1461 public static void copyURLToFile(final URL source, final File destination) throws IOException { 1462 copyInputStreamToFile(source.openStream(), destination); 1463 } 1464 1465 /** 1466 * Copies bytes from the URL <code>source</code> to a file 1467 * <code>destination</code>. The directories up to <code>destination</code> 1468 * will be created if they don't already exist. <code>destination</code> 1469 * will be overwritten if it already exists. 1470 * 1471 * @param source the <code>URL</code> to copy bytes from, must not be {@code null} 1472 * @param destination the non-directory <code>File</code> to write bytes to 1473 * (possibly overwriting), must not be {@code null} 1474 * @param connectionTimeout the number of milliseconds until this method 1475 * will timeout if no connection could be established to the <code>source</code> 1476 * @param readTimeout the number of milliseconds until this method will 1477 * timeout if no data could be read from the <code>source</code> 1478 * @throws IOException if <code>source</code> URL cannot be opened 1479 * @throws IOException if <code>destination</code> is a directory 1480 * @throws IOException if <code>destination</code> cannot be written 1481 * @throws IOException if <code>destination</code> needs creating but can't be 1482 * @throws IOException if an IO error occurs during copying 1483 * @since 2.0 1484 */ 1485 public static void copyURLToFile(final URL source, final File destination, 1486 final int connectionTimeout, final int readTimeout) throws IOException { 1487 final URLConnection connection = source.openConnection(); 1488 connection.setConnectTimeout(connectionTimeout); 1489 connection.setReadTimeout(readTimeout); 1490 copyInputStreamToFile(connection.getInputStream(), destination); 1491 } 1492 1493 /** 1494 * Copies bytes from an {@link InputStream} <code>source</code> to a file 1495 * <code>destination</code>. The directories up to <code>destination</code> 1496 * will be created if they don't already exist. <code>destination</code> 1497 * will be overwritten if it already exists. 1498 * The {@code source} stream is closed. 1499 * 1500 * @param source the <code>InputStream</code> to copy bytes from, must not be {@code null}, will be closed 1501 * @param destination the non-directory <code>File</code> to write bytes to 1502 * (possibly overwriting), must not be {@code null} 1503 * @throws IOException if <code>destination</code> is a directory 1504 * @throws IOException if <code>destination</code> cannot be written 1505 * @throws IOException if <code>destination</code> needs creating but can't be 1506 * @throws IOException if an IO error occurs during copying 1507 * @since 2.0 1508 */ 1509 public static void copyInputStreamToFile(final InputStream source, final File destination) throws IOException { 1510 try { 1511 copyToFile(source, destination); 1512 } finally { 1513 IOUtils.closeQuietly(source); 1514 } 1515 } 1516 1517 /** 1518 * Copies bytes from an {@link InputStream} <code>source</code> to a file 1519 * <code>destination</code>. The directories up to <code>destination</code> 1520 * will be created if they don't already exist. <code>destination</code> 1521 * will be overwritten if it already exists. 1522 * The {@code source} stream is closed. 1523 * 1524 * @param source the <code>InputStream</code> to copy bytes from, must not be {@code null} 1525 * @param destination the non-directory <code>File</code> to write bytes to 1526 * (possibly overwriting), must not be {@code null} 1527 * @throws IOException if <code>destination</code> is a directory 1528 * @throws IOException if <code>destination</code> cannot be written 1529 * @throws IOException if <code>destination</code> needs creating but can't be 1530 * @throws IOException if an IO error occurs during copying 1531 * @since 2.5 1532 */ 1533 public static void copyToFile(final InputStream source, final File destination) throws IOException { 1534 final FileOutputStream output = openOutputStream(destination); 1535 try { 1536 IOUtils.copy(source, output); 1537 output.close(); // don't swallow close Exception if copy completes normally 1538 } finally { 1539 IOUtils.closeQuietly(output); 1540 } 1541 } 1542 1543 //----------------------------------------------------------------------- 1544 /** 1545 * Deletes a directory recursively. 1546 * 1547 * @param directory directory to delete 1548 * @throws IOException in case deletion is unsuccessful 1549 */ 1550 public static void deleteDirectory(final File directory) throws IOException { 1551 if (!directory.exists()) { 1552 return; 1553 } 1554 1555 if (!isSymlink(directory)) { 1556 cleanDirectory(directory); 1557 } 1558 1559 if (!directory.delete()) { 1560 final String message = 1561 "Unable to delete directory " + directory + "."; 1562 throw new IOException(message); 1563 } 1564 } 1565 1566 /** 1567 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. 1568 * <p> 1569 * The difference between File.delete() and this method are: 1570 * <ul> 1571 * <li>A directory to be deleted does not have to be empty.</li> 1572 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li> 1573 * </ul> 1574 * 1575 * @param file file or directory to delete, can be {@code null} 1576 * @return {@code true} if the file or directory was deleted, otherwise 1577 * {@code false} 1578 * 1579 * @since 1.4 1580 */ 1581 public static boolean deleteQuietly(final File file) { 1582 if (file == null) { 1583 return false; 1584 } 1585 try { 1586 if (file.isDirectory()) { 1587 cleanDirectory(file); 1588 } 1589 } catch (final Exception ignored) { 1590 } 1591 1592 try { 1593 return file.delete(); 1594 } catch (final Exception ignored) { 1595 return false; 1596 } 1597 } 1598 1599 /** 1600 * Determines whether the {@code parent} directory contains the {@code child} element (a file or directory). 1601 * <p> 1602 * Files are normalized before comparison. 1603 * </p> 1604 * 1605 * Edge cases: 1606 * <ul> 1607 * <li>A {@code directory} must not be null: if null, throw IllegalArgumentException</li> 1608 * <li>A {@code directory} must be a directory: if not a directory, throw IllegalArgumentException</li> 1609 * <li>A directory does not contain itself: return false</li> 1610 * <li>A null child file is not contained in any parent: return false</li> 1611 * </ul> 1612 * 1613 * @param directory 1614 * the file to consider as the parent. 1615 * @param child 1616 * the file to consider as the child. 1617 * @return true is the candidate leaf is under by the specified composite. False otherwise. 1618 * @throws IOException 1619 * if an IO error occurs while checking the files. 1620 * @since 2.2 1621 * @see FilenameUtils#directoryContains(String, String) 1622 */ 1623 public static boolean directoryContains(final File directory, final File child) throws IOException { 1624 1625 // Fail fast against NullPointerException 1626 if (directory == null) { 1627 throw new IllegalArgumentException("Directory must not be null"); 1628 } 1629 1630 if (!directory.isDirectory()) { 1631 throw new IllegalArgumentException("Not a directory: " + directory); 1632 } 1633 1634 if (child == null) { 1635 return false; 1636 } 1637 1638 if (!directory.exists() || !child.exists()) { 1639 return false; 1640 } 1641 1642 // Canonicalize paths (normalizes relative paths) 1643 final String canonicalParent = directory.getCanonicalPath(); 1644 final String canonicalChild = child.getCanonicalPath(); 1645 1646 return FilenameUtils.directoryContains(canonicalParent, canonicalChild); 1647 } 1648 1649 /** 1650 * Cleans a directory without deleting it. 1651 * 1652 * @param directory directory to clean 1653 * @throws IOException in case cleaning is unsuccessful 1654 */ 1655 public static void cleanDirectory(final File directory) throws IOException { 1656 if (!directory.exists()) { 1657 final String message = directory + " does not exist"; 1658 throw new IllegalArgumentException(message); 1659 } 1660 1661 if (!directory.isDirectory()) { 1662 final String message = directory + " is not a directory"; 1663 throw new IllegalArgumentException(message); 1664 } 1665 1666 final File[] files = directory.listFiles(); 1667 if (files == null) { // null if security restricted 1668 throw new IOException("Failed to list contents of " + directory); 1669 } 1670 1671 IOException exception = null; 1672 for (final File file : files) { 1673 try { 1674 forceDelete(file); 1675 } catch (final IOException ioe) { 1676 exception = ioe; 1677 } 1678 } 1679 1680 if (null != exception) { 1681 throw exception; 1682 } 1683 } 1684 1685 //----------------------------------------------------------------------- 1686 /** 1687 * Waits for NFS to propagate a file creation, imposing a timeout. 1688 * <p> 1689 * This method repeatedly tests {@link File#exists()} until it returns 1690 * true up to the maximum time specified in seconds. 1691 * 1692 * @param file the file to check, must not be {@code null} 1693 * @param seconds the maximum time in seconds to wait 1694 * @return true if file exists 1695 * @throws NullPointerException if the file is {@code null} 1696 */ 1697 public static boolean waitFor(final File file, final int seconds) { 1698 int timeout = 0; 1699 int tick = 0; 1700 while (!file.exists()) { 1701 if (tick++ >= 10) { 1702 tick = 0; 1703 if (timeout++ > seconds) { 1704 return false; 1705 } 1706 } 1707 try { 1708 Thread.sleep(100); 1709 } catch (final InterruptedException ignore) { 1710 // ignore exception 1711 } catch (final Exception ex) { 1712 break; 1713 } 1714 } 1715 return true; 1716 } 1717 1718 //----------------------------------------------------------------------- 1719 /** 1720 * Reads the contents of a file into a String. 1721 * The file is always closed. 1722 * 1723 * @param file the file to read, must not be {@code null} 1724 * @param encoding the encoding to use, {@code null} means platform default 1725 * @return the file contents, never {@code null} 1726 * @throws IOException in case of an I/O error 1727 * @since 2.3 1728 */ 1729 public static String readFileToString(final File file, final Charset encoding) throws IOException { 1730 InputStream in = null; 1731 try { 1732 in = openInputStream(file); 1733 return IOUtils.toString(in, Charsets.toCharset(encoding)); 1734 } finally { 1735 IOUtils.closeQuietly(in); 1736 } 1737 } 1738 1739 /** 1740 * Reads the contents of a file into a String. The file is always closed. 1741 * 1742 * @param file 1743 * the file to read, must not be {@code null} 1744 * @param encoding 1745 * the encoding to use, {@code null} means platform default 1746 * @return the file contents, never {@code null} 1747 * @throws IOException 1748 * in case of an I/O error 1749 * @throws UnsupportedCharsetException 1750 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 1751 * supported. 1752 * @since 2.3 1753 */ 1754 public static String readFileToString(final File file, final String encoding) throws IOException { 1755 return readFileToString(file, Charsets.toCharset(encoding)); 1756 } 1757 1758 1759 /** 1760 * Reads the contents of a file into a String using the default encoding for the VM. 1761 * The file is always closed. 1762 * 1763 * @param file the file to read, must not be {@code null} 1764 * @return the file contents, never {@code null} 1765 * @throws IOException in case of an I/O error 1766 * @since 1.3.1 1767 * @deprecated 2.5 use {@link #readFileToString(File, Charset)} instead 1768 */ 1769 @Deprecated 1770 public static String readFileToString(final File file) throws IOException { 1771 return readFileToString(file, Charset.defaultCharset()); 1772 } 1773 1774 /** 1775 * Reads the contents of a file into a byte array. 1776 * The file is always closed. 1777 * 1778 * @param file the file to read, must not be {@code null} 1779 * @return the file contents, never {@code null} 1780 * @throws IOException in case of an I/O error 1781 * @since 1.1 1782 */ 1783 public static byte[] readFileToByteArray(final File file) throws IOException { 1784 InputStream in = null; 1785 try { 1786 in = openInputStream(file); 1787 return IOUtils.toByteArray(in, file.length()); 1788 } finally { 1789 IOUtils.closeQuietly(in); 1790 } 1791 } 1792 1793 /** 1794 * Reads the contents of a file line by line to a List of Strings. 1795 * The file is always closed. 1796 * 1797 * @param file the file to read, must not be {@code null} 1798 * @param encoding the encoding to use, {@code null} means platform default 1799 * @return the list of Strings representing each line in the file, never {@code null} 1800 * @throws IOException in case of an I/O error 1801 * @since 2.3 1802 */ 1803 public static List<String> readLines(final File file, final Charset encoding) throws IOException { 1804 InputStream in = null; 1805 try { 1806 in = openInputStream(file); 1807 return IOUtils.readLines(in, Charsets.toCharset(encoding)); 1808 } finally { 1809 IOUtils.closeQuietly(in); 1810 } 1811 } 1812 1813 /** 1814 * Reads the contents of a file line by line to a List of Strings. The file is always closed. 1815 * 1816 * @param file 1817 * the file to read, must not be {@code null} 1818 * @param encoding 1819 * the encoding to use, {@code null} means platform default 1820 * @return the list of Strings representing each line in the file, never {@code null} 1821 * @throws IOException 1822 * in case of an I/O error 1823 * @throws UnsupportedCharsetException 1824 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 1825 * supported. 1826 * @since 1.1 1827 */ 1828 public static List<String> readLines(final File file, final String encoding) throws IOException { 1829 return readLines(file, Charsets.toCharset(encoding)); 1830 } 1831 1832 /** 1833 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. 1834 * The file is always closed. 1835 * 1836 * @param file the file to read, must not be {@code null} 1837 * @return the list of Strings representing each line in the file, never {@code null} 1838 * @throws IOException in case of an I/O error 1839 * @since 1.3 1840 * @deprecated 2.5 use {@link #readLines(File, Charset)} instead 1841 */ 1842 @Deprecated 1843 public static List<String> readLines(final File file) throws IOException { 1844 return readLines(file, Charset.defaultCharset()); 1845 } 1846 1847 /** 1848 * Returns an Iterator for the lines in a <code>File</code>. 1849 * <p> 1850 * This method opens an <code>InputStream</code> for the file. 1851 * When you have finished with the iterator you should close the stream 1852 * to free internal resources. This can be done by calling the 1853 * {@link LineIterator#close()} or 1854 * {@link LineIterator#closeQuietly(LineIterator)} method. 1855 * <p> 1856 * The recommended usage pattern is: 1857 * <pre> 1858 * LineIterator it = FileUtils.lineIterator(file, "UTF-8"); 1859 * try { 1860 * while (it.hasNext()) { 1861 * String line = it.nextLine(); 1862 * /// do something with line 1863 * } 1864 * } finally { 1865 * LineIterator.closeQuietly(iterator); 1866 * } 1867 * </pre> 1868 * <p> 1869 * If an exception occurs during the creation of the iterator, the 1870 * underlying stream is closed. 1871 * 1872 * @param file the file to open for input, must not be {@code null} 1873 * @param encoding the encoding to use, {@code null} means platform default 1874 * @return an Iterator of the lines in the file, never {@code null} 1875 * @throws IOException in case of an I/O error (file closed) 1876 * @since 1.2 1877 */ 1878 public static LineIterator lineIterator(final File file, final String encoding) throws IOException { 1879 InputStream in = null; 1880 try { 1881 in = openInputStream(file); 1882 return IOUtils.lineIterator(in, encoding); 1883 } catch (final IOException ex) { 1884 IOUtils.closeQuietly(in); 1885 throw ex; 1886 } catch (final RuntimeException ex) { 1887 IOUtils.closeQuietly(in); 1888 throw ex; 1889 } 1890 } 1891 1892 /** 1893 * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM. 1894 * 1895 * @param file the file to open for input, must not be {@code null} 1896 * @return an Iterator of the lines in the file, never {@code null} 1897 * @throws IOException in case of an I/O error (file closed) 1898 * @since 1.3 1899 * @see #lineIterator(File, String) 1900 */ 1901 public static LineIterator lineIterator(final File file) throws IOException { 1902 return lineIterator(file, null); 1903 } 1904 1905 //----------------------------------------------------------------------- 1906 /** 1907 * Writes a String to a file creating the file if it does not exist. 1908 * 1909 * NOTE: As from v1.3, the parent directories of the file will be created 1910 * if they do not exist. 1911 * 1912 * @param file the file to write 1913 * @param data the content to write to the file 1914 * @param encoding the encoding to use, {@code null} means platform default 1915 * @throws IOException in case of an I/O error 1916 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1917 * @since 2.4 1918 */ 1919 public static void writeStringToFile(final File file, final String data, final Charset encoding) throws IOException { 1920 writeStringToFile(file, data, encoding, false); 1921 } 1922 1923 /** 1924 * Writes a String to a file creating the file if it does not exist. 1925 * 1926 * NOTE: As from v1.3, the parent directories of the file will be created 1927 * if they do not exist. 1928 * 1929 * @param file the file to write 1930 * @param data the content to write to the file 1931 * @param encoding the encoding to use, {@code null} means platform default 1932 * @throws IOException in case of an I/O error 1933 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1934 */ 1935 public static void writeStringToFile(final File file, final String data, final String encoding) throws IOException { 1936 writeStringToFile(file, data, encoding, false); 1937 } 1938 1939 /** 1940 * Writes a String to a file creating the file if it does not exist. 1941 * 1942 * @param file the file to write 1943 * @param data the content to write to the file 1944 * @param encoding the encoding to use, {@code null} means platform default 1945 * @param append if {@code true}, then the String will be added to the 1946 * end of the file rather than overwriting 1947 * @throws IOException in case of an I/O error 1948 * @since 2.3 1949 */ 1950 public static void writeStringToFile(final File file, final String data, final Charset encoding, final boolean append) throws IOException { 1951 OutputStream out = null; 1952 try { 1953 out = openOutputStream(file, append); 1954 IOUtils.write(data, out, encoding); 1955 out.close(); // don't swallow close Exception if copy completes normally 1956 } finally { 1957 IOUtils.closeQuietly(out); 1958 } 1959 } 1960 1961 /** 1962 * Writes a String to a file creating the file if it does not exist. 1963 * 1964 * @param file the file to write 1965 * @param data the content to write to the file 1966 * @param encoding the encoding to use, {@code null} means platform default 1967 * @param append if {@code true}, then the String will be added to the 1968 * end of the file rather than overwriting 1969 * @throws IOException in case of an I/O error 1970 * @throws UnsupportedCharsetException 1971 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 1972 * supported by the VM 1973 * @since 2.1 1974 */ 1975 public static void writeStringToFile(final File file, final String data, final String encoding, final boolean append) throws IOException { 1976 writeStringToFile(file, data, Charsets.toCharset(encoding), append); 1977 } 1978 1979 /** 1980 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1981 * 1982 * @param file the file to write 1983 * @param data the content to write to the file 1984 * @throws IOException in case of an I/O error 1985 * @deprecated 2.5 use {@link #writeStringToFile(File, String, Charset)} instead 1986 */ 1987 @Deprecated 1988 public static void writeStringToFile(final File file, final String data) throws IOException { 1989 writeStringToFile(file, data, Charset.defaultCharset(), false); 1990 } 1991 1992 /** 1993 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1994 * 1995 * @param file the file to write 1996 * @param data the content to write to the file 1997 * @param append if {@code true}, then the String will be added to the 1998 * end of the file rather than overwriting 1999 * @throws IOException in case of an I/O error 2000 * @since 2.1 2001 * @deprecated 2.5 use {@link #writeStringToFile(File, String, Charset, boolean)} instead 2002 */ 2003 @Deprecated 2004 public static void writeStringToFile(final File file, final String data, final boolean append) throws IOException { 2005 writeStringToFile(file, data, Charset.defaultCharset(), append); 2006 } 2007 2008 /** 2009 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. 2010 * 2011 * @param file the file to write 2012 * @param data the content to write to the file 2013 * @throws IOException in case of an I/O error 2014 * @since 2.0 2015 * @deprecated 2.5 use {@link #write(File, CharSequence, Charset)} instead 2016 */ 2017 @Deprecated 2018 public static void write(final File file, final CharSequence data) throws IOException { 2019 write(file, data, Charset.defaultCharset(), false); 2020 } 2021 2022 /** 2023 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. 2024 * 2025 * @param file the file to write 2026 * @param data the content to write to the file 2027 * @param append if {@code true}, then the data will be added to the 2028 * end of the file rather than overwriting 2029 * @throws IOException in case of an I/O error 2030 * @since 2.1 2031 * @deprecated 2.5 use {@link #write(File, CharSequence, Charset, boolean)} instead 2032 */ 2033 @Deprecated 2034 public static void write(final File file, final CharSequence data, final boolean append) throws IOException { 2035 write(file, data, Charset.defaultCharset(), append); 2036 } 2037 2038 /** 2039 * Writes a CharSequence to a file creating the file if it does not exist. 2040 * 2041 * @param file the file to write 2042 * @param data the content to write to the file 2043 * @param encoding the encoding to use, {@code null} means platform default 2044 * @throws IOException in case of an I/O error 2045 * @since 2.3 2046 */ 2047 public static void write(final File file, final CharSequence data, final Charset encoding) throws IOException { 2048 write(file, data, encoding, false); 2049 } 2050 2051 /** 2052 * Writes a CharSequence to a file creating the file if it does not exist. 2053 * 2054 * @param file the file to write 2055 * @param data the content to write to the file 2056 * @param encoding the encoding to use, {@code null} means platform default 2057 * @throws IOException in case of an I/O error 2058 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2059 * @since 2.0 2060 */ 2061 public static void write(final File file, final CharSequence data, final String encoding) throws IOException { 2062 write(file, data, encoding, false); 2063 } 2064 2065 /** 2066 * Writes a CharSequence to a file creating the file if it does not exist. 2067 * 2068 * @param file the file to write 2069 * @param data the content to write to the file 2070 * @param encoding the encoding to use, {@code null} means platform default 2071 * @param append if {@code true}, then the data will be added to the 2072 * end of the file rather than overwriting 2073 * @throws IOException in case of an I/O error 2074 * @since 2.3 2075 */ 2076 public static void write(final File file, final CharSequence data, final Charset encoding, final boolean append) throws IOException { 2077 final String str = data == null ? null : data.toString(); 2078 writeStringToFile(file, str, encoding, append); 2079 } 2080 2081 /** 2082 * Writes a CharSequence to a file creating the file if it does not exist. 2083 * 2084 * @param file the file to write 2085 * @param data the content to write to the file 2086 * @param encoding the encoding to use, {@code null} means platform default 2087 * @param append if {@code true}, then the data will be added to the 2088 * end of the file rather than overwriting 2089 * @throws IOException in case of an I/O error 2090 * @throws UnsupportedCharsetException 2091 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 2092 * supported by the VM 2093 * @since 2.1 2094 */ 2095 public static void write(final File file, final CharSequence data, final String encoding, final boolean append) throws IOException { 2096 write(file, data, Charsets.toCharset(encoding), append); 2097 } 2098 2099 /** 2100 * Writes a byte array to a file creating the file if it does not exist. 2101 * <p> 2102 * NOTE: As from v1.3, the parent directories of the file will be created 2103 * if they do not exist. 2104 * 2105 * @param file the file to write to 2106 * @param data the content to write to the file 2107 * @throws IOException in case of an I/O error 2108 * @since 1.1 2109 */ 2110 public static void writeByteArrayToFile(final File file, final byte[] data) throws IOException { 2111 writeByteArrayToFile(file, data, false); 2112 } 2113 2114 /** 2115 * Writes a byte array to a file creating the file if it does not exist. 2116 * 2117 * @param file the file to write to 2118 * @param data the content to write to the file 2119 * @param append if {@code true}, then bytes will be added to the 2120 * end of the file rather than overwriting 2121 * @throws IOException in case of an I/O error 2122 * @since 2.1 2123 */ 2124 public static void writeByteArrayToFile(final File file, final byte[] data, final boolean append) throws IOException { 2125 writeByteArrayToFile(file, data, 0, data.length, append); 2126 } 2127 2128 /** 2129 * Writes {@code len} bytes from the specified byte array starting 2130 * at offset {@code off} to a file, creating the file if it does 2131 * not exist. 2132 * 2133 * @param file the file to write to 2134 * @param data the content to write to the file 2135 * @param off the start offset in the data 2136 * @param len the number of bytes to write 2137 * @throws IOException in case of an I/O error 2138 * @since 2.5 2139 */ 2140 public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len) throws IOException { 2141 writeByteArrayToFile(file, data, off, len, false); 2142 } 2143 2144 /** 2145 * Writes {@code len} bytes from the specified byte array starting 2146 * at offset {@code off} to a file, creating the file if it does 2147 * not exist. 2148 * 2149 * @param file the file to write to 2150 * @param data the content to write to the file 2151 * @param off the start offset in the data 2152 * @param len the number of bytes to write 2153 * @param append if {@code true}, then bytes will be added to the 2154 * end of the file rather than overwriting 2155 * @throws IOException in case of an I/O error 2156 * @since 2.5 2157 */ 2158 public static void writeByteArrayToFile(final File file, final byte[] data, final int off, final int len, final boolean append) throws IOException { 2159 OutputStream out = null; 2160 try { 2161 out = openOutputStream(file, append); 2162 out.write(data, off, len); 2163 out.close(); // don't swallow close Exception if copy completes normally 2164 } finally { 2165 IOUtils.closeQuietly(out); 2166 } 2167 } 2168 2169 /** 2170 * Writes the <code>toString()</code> value of each item in a collection to 2171 * the specified <code>File</code> line by line. 2172 * The specified character encoding and the default line ending will be used. 2173 * <p> 2174 * NOTE: As from v1.3, the parent directories of the file will be created 2175 * if they do not exist. 2176 * 2177 * @param file the file to write to 2178 * @param encoding the encoding to use, {@code null} means platform default 2179 * @param lines the lines to write, {@code null} entries produce blank lines 2180 * @throws IOException in case of an I/O error 2181 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2182 * @since 1.1 2183 */ 2184 public static void writeLines(final File file, final String encoding, final Collection<?> lines) throws IOException { 2185 writeLines(file, encoding, lines, null, false); 2186 } 2187 2188 /** 2189 * Writes the <code>toString()</code> value of each item in a collection to 2190 * the specified <code>File</code> line by line, optionally appending. 2191 * The specified character encoding and the default line ending will be used. 2192 * 2193 * @param file the file to write to 2194 * @param encoding the encoding to use, {@code null} means platform default 2195 * @param lines the lines to write, {@code null} entries produce blank lines 2196 * @param append if {@code true}, then the lines will be added to the 2197 * end of the file rather than overwriting 2198 * @throws IOException in case of an I/O error 2199 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2200 * @since 2.1 2201 */ 2202 public static void writeLines(final File file, final String encoding, final Collection<?> lines, final boolean append) throws IOException { 2203 writeLines(file, encoding, lines, null, append); 2204 } 2205 2206 /** 2207 * Writes the <code>toString()</code> value of each item in a collection to 2208 * the specified <code>File</code> line by line. 2209 * The default VM encoding and the default line ending will be used. 2210 * 2211 * @param file the file to write to 2212 * @param lines the lines to write, {@code null} entries produce blank lines 2213 * @throws IOException in case of an I/O error 2214 * @since 1.3 2215 */ 2216 public static void writeLines(final File file, final Collection<?> lines) throws IOException { 2217 writeLines(file, null, lines, null, false); 2218 } 2219 2220 /** 2221 * Writes the <code>toString()</code> value of each item in a collection to 2222 * the specified <code>File</code> line by line. 2223 * The default VM encoding and the default line ending will be used. 2224 * 2225 * @param file the file to write to 2226 * @param lines the lines to write, {@code null} entries produce blank lines 2227 * @param append if {@code true}, then the lines will be added to the 2228 * end of the file rather than overwriting 2229 * @throws IOException in case of an I/O error 2230 * @since 2.1 2231 */ 2232 public static void writeLines(final File file, final Collection<?> lines, final boolean append) throws IOException { 2233 writeLines(file, null, lines, null, append); 2234 } 2235 2236 /** 2237 * Writes the <code>toString()</code> value of each item in a collection to 2238 * the specified <code>File</code> line by line. 2239 * The specified character encoding and the line ending will be used. 2240 * <p> 2241 * NOTE: As from v1.3, the parent directories of the file will be created 2242 * if they do not exist. 2243 * 2244 * @param file the file to write to 2245 * @param encoding the encoding to use, {@code null} means platform default 2246 * @param lines the lines to write, {@code null} entries produce blank lines 2247 * @param lineEnding the line separator to use, {@code null} is system default 2248 * @throws IOException in case of an I/O error 2249 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2250 * @since 1.1 2251 */ 2252 public static void writeLines(final File file, final String encoding, final Collection<?> lines, final String lineEnding) 2253 throws IOException { 2254 writeLines(file, encoding, lines, lineEnding, false); 2255 } 2256 2257 /** 2258 * Writes the <code>toString()</code> value of each item in a collection to 2259 * the specified <code>File</code> line by line. 2260 * The specified character encoding and the line ending will be used. 2261 * 2262 * @param file the file to write to 2263 * @param encoding the encoding to use, {@code null} means platform default 2264 * @param lines the lines to write, {@code null} entries produce blank lines 2265 * @param lineEnding the line separator to use, {@code null} is system default 2266 * @param append if {@code true}, then the lines will be added to the 2267 * end of the file rather than overwriting 2268 * @throws IOException in case of an I/O error 2269 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2270 * @since 2.1 2271 */ 2272 public static void writeLines(final File file, final String encoding, final Collection<?> lines, final String lineEnding, final boolean append) 2273 throws IOException { 2274 FileOutputStream out = null; 2275 try { 2276 out = openOutputStream(file, append); 2277 final BufferedOutputStream buffer = new BufferedOutputStream(out); 2278 IOUtils.writeLines(lines, lineEnding, buffer, encoding); 2279 buffer.flush(); 2280 out.close(); // don't swallow close Exception if copy completes normally 2281 } finally { 2282 IOUtils.closeQuietly(out); 2283 } 2284 } 2285 2286 /** 2287 * Writes the <code>toString()</code> value of each item in a collection to 2288 * the specified <code>File</code> line by line. 2289 * The default VM encoding and the specified line ending will be used. 2290 * 2291 * @param file the file to write to 2292 * @param lines the lines to write, {@code null} entries produce blank lines 2293 * @param lineEnding the line separator to use, {@code null} is system default 2294 * @throws IOException in case of an I/O error 2295 * @since 1.3 2296 */ 2297 public static void writeLines(final File file, final Collection<?> lines, final String lineEnding) throws IOException { 2298 writeLines(file, null, lines, lineEnding, false); 2299 } 2300 2301 /** 2302 * Writes the <code>toString()</code> value of each item in a collection to 2303 * the specified <code>File</code> line by line. 2304 * The default VM encoding and the specified line ending will be used. 2305 * 2306 * @param file the file to write to 2307 * @param lines the lines to write, {@code null} entries produce blank lines 2308 * @param lineEnding the line separator to use, {@code null} is system default 2309 * @param append if {@code true}, then the lines will be added to the 2310 * end of the file rather than overwriting 2311 * @throws IOException in case of an I/O error 2312 * @since 2.1 2313 */ 2314 public static void writeLines(final File file, final Collection<?> lines, final String lineEnding, final boolean append) 2315 throws IOException { 2316 writeLines(file, null, lines, lineEnding, append); 2317 } 2318 2319 //----------------------------------------------------------------------- 2320 /** 2321 * Deletes a file. If file is a directory, delete it and all sub-directories. 2322 * <p> 2323 * The difference between File.delete() and this method are: 2324 * <ul> 2325 * <li>A directory to be deleted does not have to be empty.</li> 2326 * <li>You get exceptions when a file or directory cannot be deleted. 2327 * (java.io.File methods returns a boolean)</li> 2328 * </ul> 2329 * 2330 * @param file file or directory to delete, must not be {@code null} 2331 * @throws NullPointerException if the directory is {@code null} 2332 * @throws FileNotFoundException if the file was not found 2333 * @throws IOException in case deletion is unsuccessful 2334 */ 2335 public static void forceDelete(final File file) throws IOException { 2336 if (file.isDirectory()) { 2337 deleteDirectory(file); 2338 } else { 2339 final boolean filePresent = file.exists(); 2340 if (!file.delete()) { 2341 if (!filePresent){ 2342 throw new FileNotFoundException("File does not exist: " + file); 2343 } 2344 final String message = 2345 "Unable to delete file: " + file; 2346 throw new IOException(message); 2347 } 2348 } 2349 } 2350 2351 /** 2352 * Schedules a file to be deleted when JVM exits. 2353 * If file is directory delete it and all sub-directories. 2354 * 2355 * @param file file or directory to delete, must not be {@code null} 2356 * @throws NullPointerException if the file is {@code null} 2357 * @throws IOException in case deletion is unsuccessful 2358 */ 2359 public static void forceDeleteOnExit(final File file) throws IOException { 2360 if (file.isDirectory()) { 2361 deleteDirectoryOnExit(file); 2362 } else { 2363 file.deleteOnExit(); 2364 } 2365 } 2366 2367 /** 2368 * Schedules a directory recursively for deletion on JVM exit. 2369 * 2370 * @param directory directory to delete, must not be {@code null} 2371 * @throws NullPointerException if the directory is {@code null} 2372 * @throws IOException in case deletion is unsuccessful 2373 */ 2374 private static void deleteDirectoryOnExit(final File directory) throws IOException { 2375 if (!directory.exists()) { 2376 return; 2377 } 2378 2379 directory.deleteOnExit(); 2380 if (!isSymlink(directory)) { 2381 cleanDirectoryOnExit(directory); 2382 } 2383 } 2384 2385 /** 2386 * Cleans a directory without deleting it. 2387 * 2388 * @param directory directory to clean, must not be {@code null} 2389 * @throws NullPointerException if the directory is {@code null} 2390 * @throws IOException in case cleaning is unsuccessful 2391 */ 2392 private static void cleanDirectoryOnExit(final File directory) throws IOException { 2393 if (!directory.exists()) { 2394 final String message = directory + " does not exist"; 2395 throw new IllegalArgumentException(message); 2396 } 2397 2398 if (!directory.isDirectory()) { 2399 final String message = directory + " is not a directory"; 2400 throw new IllegalArgumentException(message); 2401 } 2402 2403 final File[] files = directory.listFiles(); 2404 if (files == null) { // null if security restricted 2405 throw new IOException("Failed to list contents of " + directory); 2406 } 2407 2408 IOException exception = null; 2409 for (final File file : files) { 2410 try { 2411 forceDeleteOnExit(file); 2412 } catch (final IOException ioe) { 2413 exception = ioe; 2414 } 2415 } 2416 2417 if (null != exception) { 2418 throw exception; 2419 } 2420 } 2421 2422 /** 2423 * Makes a directory, including any necessary but nonexistent parent 2424 * directories. If a file already exists with specified name but it is 2425 * not a directory then an IOException is thrown. 2426 * If the directory cannot be created (or does not already exist) 2427 * then an IOException is thrown. 2428 * 2429 * @param directory directory to create, must not be {@code null} 2430 * @throws NullPointerException if the directory is {@code null} 2431 * @throws IOException if the directory cannot be created or the file already exists but is not a directory 2432 */ 2433 public static void forceMkdir(final File directory) throws IOException { 2434 if (directory.exists()) { 2435 if (!directory.isDirectory()) { 2436 final String message = 2437 "File " 2438 + directory 2439 + " exists and is " 2440 + "not a directory. Unable to create directory."; 2441 throw new IOException(message); 2442 } 2443 } else { 2444 if (!directory.mkdirs()) { 2445 // Double-check that some other thread or process hasn't made 2446 // the directory in the background 2447 if (!directory.isDirectory()) 2448 { 2449 final String message = 2450 "Unable to create directory " + directory; 2451 throw new IOException(message); 2452 } 2453 } 2454 } 2455 } 2456 2457 /** 2458 * Makes any necessary but nonexistent parent directories for a given File. If the parent directory cannot be 2459 * created then an IOException is thrown. 2460 * 2461 * @param file 2462 * file with parent to create, must not be {@code null} 2463 * @throws NullPointerException 2464 * if the file is {@code null} 2465 * @throws IOException 2466 * if the parent directory cannot be created 2467 * @since 2.5 2468 */ 2469 public static void forceMkdirParent(final File file) throws IOException { 2470 final File parent = file.getParentFile(); 2471 if (parent == null) { 2472 return; 2473 } 2474 forceMkdir(parent); 2475 } 2476 2477 //----------------------------------------------------------------------- 2478 /** 2479 * Returns the size of the specified file or directory. If the provided 2480 * {@link File} is a regular file, then the file's length is returned. 2481 * If the argument is a directory, then the size of the directory is 2482 * calculated recursively. If a directory or subdirectory is security 2483 * restricted, its size will not be included. 2484 * <p> 2485 * Note that overflow is not detected, and the return value may be negative if 2486 * overflow occurs. See {@link #sizeOfAsBigInteger(File)} for an alternative 2487 * method that does not overflow. 2488 * 2489 * @param file the regular file or directory to return the size 2490 * of (must not be {@code null}). 2491 * 2492 * @return the length of the file, or recursive size of the directory, 2493 * provided (in bytes). 2494 * 2495 * @throws NullPointerException if the file is {@code null} 2496 * @throws IllegalArgumentException if the file does not exist. 2497 * 2498 * @since 2.0 2499 */ 2500 public static long sizeOf(final File file) { 2501 2502 if (!file.exists()) { 2503 final String message = file + " does not exist"; 2504 throw new IllegalArgumentException(message); 2505 } 2506 2507 if (file.isDirectory()) { 2508 return sizeOfDirectory(file); 2509 } else { 2510 return file.length(); 2511 } 2512 2513 } 2514 2515 /** 2516 * Returns the size of the specified file or directory. If the provided 2517 * {@link File} is a regular file, then the file's length is returned. 2518 * If the argument is a directory, then the size of the directory is 2519 * calculated recursively. If a directory or subdirectory is security 2520 * restricted, its size will not be included. 2521 * 2522 * @param file the regular file or directory to return the size 2523 * of (must not be {@code null}). 2524 * 2525 * @return the length of the file, or recursive size of the directory, 2526 * provided (in bytes). 2527 * 2528 * @throws NullPointerException if the file is {@code null} 2529 * @throws IllegalArgumentException if the file does not exist. 2530 * 2531 * @since 2.4 2532 */ 2533 public static BigInteger sizeOfAsBigInteger(final File file) { 2534 2535 if (!file.exists()) { 2536 final String message = file + " does not exist"; 2537 throw new IllegalArgumentException(message); 2538 } 2539 2540 if (file.isDirectory()) { 2541 return sizeOfDirectoryAsBigInteger(file); 2542 } else { 2543 return BigInteger.valueOf(file.length()); 2544 } 2545 2546 } 2547 2548 /** 2549 * Counts the size of a directory recursively (sum of the length of all files). 2550 * <p> 2551 * Note that overflow is not detected, and the return value may be negative if 2552 * overflow occurs. See {@link #sizeOfDirectoryAsBigInteger(File)} for an alternative 2553 * method that does not overflow. 2554 * 2555 * 2556 * @param directory 2557 * directory to inspect, must not be {@code null} 2558 * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total 2559 * is greater than {@link Long#MAX_VALUE}. 2560 * @throws NullPointerException 2561 * if the directory is {@code null} 2562 */ 2563 public static long sizeOfDirectory(final File directory) { 2564 checkDirectory(directory); 2565 2566 final File[] files = directory.listFiles(); 2567 if (files == null) { // null if security restricted 2568 return 0L; 2569 } 2570 long size = 0; 2571 2572 for (final File file : files) { 2573 try { 2574 if (!isSymlink(file)) { 2575 size += sizeOf(file); 2576 if (size < 0) { 2577 break; 2578 } 2579 } 2580 } catch (final IOException ioe) { 2581 // Ignore exceptions caught when asking if a File is a symlink. 2582 } 2583 } 2584 2585 return size; 2586 } 2587 2588 /** 2589 * Counts the size of a directory recursively (sum of the length of all files). 2590 * 2591 * @param directory 2592 * directory to inspect, must not be {@code null} 2593 * @return size of directory in bytes, 0 if directory is security restricted. 2594 * @throws NullPointerException 2595 * if the directory is {@code null} 2596 * @since 2.4 2597 */ 2598 public static BigInteger sizeOfDirectoryAsBigInteger(final File directory) { 2599 checkDirectory(directory); 2600 2601 final File[] files = directory.listFiles(); 2602 if (files == null) { // null if security restricted 2603 return BigInteger.ZERO; 2604 } 2605 BigInteger size = BigInteger.ZERO; 2606 2607 for (final File file : files) { 2608 try { 2609 if (!isSymlink(file)) { 2610 size = size.add(BigInteger.valueOf(sizeOf(file))); 2611 } 2612 } catch (final IOException ioe) { 2613 // Ignore exceptions caught when asking if a File is a symlink. 2614 } 2615 } 2616 2617 return size; 2618 } 2619 2620 /** 2621 * Checks that the given {@code File} exists and is a directory. 2622 * 2623 * @param directory The {@code File} to check. 2624 * @throws IllegalArgumentException if the given {@code File} does not exist or is not a directory. 2625 */ 2626 private static void checkDirectory(final File directory) { 2627 if (!directory.exists()) { 2628 throw new IllegalArgumentException(directory + " does not exist"); 2629 } 2630 if (!directory.isDirectory()) { 2631 throw new IllegalArgumentException(directory + " is not a directory"); 2632 } 2633 } 2634 2635 //----------------------------------------------------------------------- 2636 /** 2637 * Tests if the specified <code>File</code> is newer than the reference 2638 * <code>File</code>. 2639 * 2640 * @param file the <code>File</code> of which the modification date must 2641 * be compared, must not be {@code null} 2642 * @param reference the <code>File</code> of which the modification date 2643 * is used, must not be {@code null} 2644 * @return true if the <code>File</code> exists and has been modified more 2645 * recently than the reference <code>File</code> 2646 * @throws IllegalArgumentException if the file is {@code null} 2647 * @throws IllegalArgumentException if the reference file is {@code null} or doesn't exist 2648 */ 2649 public static boolean isFileNewer(final File file, final File reference) { 2650 if (reference == null) { 2651 throw new IllegalArgumentException("No specified reference file"); 2652 } 2653 if (!reference.exists()) { 2654 throw new IllegalArgumentException("The reference file '" 2655 + reference + "' doesn't exist"); 2656 } 2657 return isFileNewer(file, reference.lastModified()); 2658 } 2659 2660 /** 2661 * Tests if the specified <code>File</code> is newer than the specified 2662 * <code>Date</code>. 2663 * 2664 * @param file the <code>File</code> of which the modification date 2665 * must be compared, must not be {@code null} 2666 * @param date the date reference, must not be {@code null} 2667 * @return true if the <code>File</code> exists and has been modified 2668 * after the given <code>Date</code>. 2669 * @throws IllegalArgumentException if the file is {@code null} 2670 * @throws IllegalArgumentException if the date is {@code null} 2671 */ 2672 public static boolean isFileNewer(final File file, final Date date) { 2673 if (date == null) { 2674 throw new IllegalArgumentException("No specified date"); 2675 } 2676 return isFileNewer(file, date.getTime()); 2677 } 2678 2679 /** 2680 * Tests if the specified <code>File</code> is newer than the specified 2681 * time reference. 2682 * 2683 * @param file the <code>File</code> of which the modification date must 2684 * be compared, must not be {@code null} 2685 * @param timeMillis the time reference measured in milliseconds since the 2686 * epoch (00:00:00 GMT, January 1, 1970) 2687 * @return true if the <code>File</code> exists and has been modified after 2688 * the given time reference. 2689 * @throws IllegalArgumentException if the file is {@code null} 2690 */ 2691 public static boolean isFileNewer(final File file, final long timeMillis) { 2692 if (file == null) { 2693 throw new IllegalArgumentException("No specified file"); 2694 } 2695 if (!file.exists()) { 2696 return false; 2697 } 2698 return file.lastModified() > timeMillis; 2699 } 2700 2701 2702 //----------------------------------------------------------------------- 2703 /** 2704 * Tests if the specified <code>File</code> is older than the reference 2705 * <code>File</code>. 2706 * 2707 * @param file the <code>File</code> of which the modification date must 2708 * be compared, must not be {@code null} 2709 * @param reference the <code>File</code> of which the modification date 2710 * is used, must not be {@code null} 2711 * @return true if the <code>File</code> exists and has been modified before 2712 * the reference <code>File</code> 2713 * @throws IllegalArgumentException if the file is {@code null} 2714 * @throws IllegalArgumentException if the reference file is {@code null} or doesn't exist 2715 */ 2716 public static boolean isFileOlder(final File file, final File reference) { 2717 if (reference == null) { 2718 throw new IllegalArgumentException("No specified reference file"); 2719 } 2720 if (!reference.exists()) { 2721 throw new IllegalArgumentException("The reference file '" 2722 + reference + "' doesn't exist"); 2723 } 2724 return isFileOlder(file, reference.lastModified()); 2725 } 2726 2727 /** 2728 * Tests if the specified <code>File</code> is older than the specified 2729 * <code>Date</code>. 2730 * 2731 * @param file the <code>File</code> of which the modification date 2732 * must be compared, must not be {@code null} 2733 * @param date the date reference, must not be {@code null} 2734 * @return true if the <code>File</code> exists and has been modified 2735 * before the given <code>Date</code>. 2736 * @throws IllegalArgumentException if the file is {@code null} 2737 * @throws IllegalArgumentException if the date is {@code null} 2738 */ 2739 public static boolean isFileOlder(final File file, final Date date) { 2740 if (date == null) { 2741 throw new IllegalArgumentException("No specified date"); 2742 } 2743 return isFileOlder(file, date.getTime()); 2744 } 2745 2746 /** 2747 * Tests if the specified <code>File</code> is older than the specified 2748 * time reference. 2749 * 2750 * @param file the <code>File</code> of which the modification date must 2751 * be compared, must not be {@code null} 2752 * @param timeMillis the time reference measured in milliseconds since the 2753 * epoch (00:00:00 GMT, January 1, 1970) 2754 * @return true if the <code>File</code> exists and has been modified before 2755 * the given time reference. 2756 * @throws IllegalArgumentException if the file is {@code null} 2757 */ 2758 public static boolean isFileOlder(final File file, final long timeMillis) { 2759 if (file == null) { 2760 throw new IllegalArgumentException("No specified file"); 2761 } 2762 if (!file.exists()) { 2763 return false; 2764 } 2765 return file.lastModified() < timeMillis; 2766 } 2767 2768 //----------------------------------------------------------------------- 2769 /** 2770 * Computes the checksum of a file using the CRC32 checksum routine. 2771 * The value of the checksum is returned. 2772 * 2773 * @param file the file to checksum, must not be {@code null} 2774 * @return the checksum value 2775 * @throws NullPointerException if the file or checksum is {@code null} 2776 * @throws IllegalArgumentException if the file is a directory 2777 * @throws IOException if an IO error occurs reading the file 2778 * @since 1.3 2779 */ 2780 public static long checksumCRC32(final File file) throws IOException { 2781 final CRC32 crc = new CRC32(); 2782 checksum(file, crc); 2783 return crc.getValue(); 2784 } 2785 2786 /** 2787 * Computes the checksum of a file using the specified checksum object. 2788 * Multiple files may be checked using one <code>Checksum</code> instance 2789 * if desired simply by reusing the same checksum object. 2790 * For example: 2791 * <pre> 2792 * long csum = FileUtils.checksum(file, new CRC32()).getValue(); 2793 * </pre> 2794 * 2795 * @param file the file to checksum, must not be {@code null} 2796 * @param checksum the checksum object to be used, must not be {@code null} 2797 * @return the checksum specified, updated with the content of the file 2798 * @throws NullPointerException if the file or checksum is {@code null} 2799 * @throws IllegalArgumentException if the file is a directory 2800 * @throws IOException if an IO error occurs reading the file 2801 * @since 1.3 2802 */ 2803 public static Checksum checksum(final File file, final Checksum checksum) throws IOException { 2804 if (file.isDirectory()) { 2805 throw new IllegalArgumentException("Checksums can't be computed on directories"); 2806 } 2807 InputStream in = null; 2808 try { 2809 in = new CheckedInputStream(new FileInputStream(file), checksum); 2810 IOUtils.copy(in, new NullOutputStream()); 2811 } finally { 2812 IOUtils.closeQuietly(in); 2813 } 2814 return checksum; 2815 } 2816 2817 /** 2818 * Moves a directory. 2819 * <p> 2820 * When the destination directory is on another file system, do a "copy and delete". 2821 * 2822 * @param srcDir the directory to be moved 2823 * @param destDir the destination directory 2824 * @throws NullPointerException if source or destination is {@code null} 2825 * @throws FileExistsException if the destination directory exists 2826 * @throws IOException if source or destination is invalid 2827 * @throws IOException if an IO error occurs moving the file 2828 * @since 1.4 2829 */ 2830 public static void moveDirectory(final File srcDir, final File destDir) throws IOException { 2831 if (srcDir == null) { 2832 throw new NullPointerException("Source must not be null"); 2833 } 2834 if (destDir == null) { 2835 throw new NullPointerException("Destination must not be null"); 2836 } 2837 if (!srcDir.exists()) { 2838 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 2839 } 2840 if (!srcDir.isDirectory()) { 2841 throw new IOException("Source '" + srcDir + "' is not a directory"); 2842 } 2843 if (destDir.exists()) { 2844 throw new FileExistsException("Destination '" + destDir + "' already exists"); 2845 } 2846 final boolean rename = srcDir.renameTo(destDir); 2847 if (!rename) { 2848 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath() + File.separator)) { 2849 throw new IOException("Cannot move directory: "+srcDir+" to a subdirectory of itself: "+destDir); 2850 } 2851 copyDirectory( srcDir, destDir ); 2852 deleteDirectory( srcDir ); 2853 if (srcDir.exists()) { 2854 throw new IOException("Failed to delete original directory '" + srcDir + 2855 "' after copy to '" + destDir + "'"); 2856 } 2857 } 2858 } 2859 2860 /** 2861 * Moves a directory to another directory. 2862 * 2863 * @param src the file to be moved 2864 * @param destDir the destination file 2865 * @param createDestDir If {@code true} create the destination directory, 2866 * otherwise if {@code false} throw an IOException 2867 * @throws NullPointerException if source or destination is {@code null} 2868 * @throws FileExistsException if the directory exists in the destination directory 2869 * @throws IOException if source or destination is invalid 2870 * @throws IOException if an IO error occurs moving the file 2871 * @since 1.4 2872 */ 2873 public static void moveDirectoryToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException { 2874 if (src == null) { 2875 throw new NullPointerException("Source must not be null"); 2876 } 2877 if (destDir == null) { 2878 throw new NullPointerException("Destination directory must not be null"); 2879 } 2880 if (!destDir.exists() && createDestDir) { 2881 destDir.mkdirs(); 2882 } 2883 if (!destDir.exists()) { 2884 throw new FileNotFoundException("Destination directory '" + destDir + 2885 "' does not exist [createDestDir=" + createDestDir +"]"); 2886 } 2887 if (!destDir.isDirectory()) { 2888 throw new IOException("Destination '" + destDir + "' is not a directory"); 2889 } 2890 moveDirectory(src, new File(destDir, src.getName())); 2891 2892 } 2893 2894 /** 2895 * Moves a file. 2896 * <p> 2897 * When the destination file is on another file system, do a "copy and delete". 2898 * 2899 * @param srcFile the file to be moved 2900 * @param destFile the destination file 2901 * @throws NullPointerException if source or destination is {@code null} 2902 * @throws FileExistsException if the destination file exists 2903 * @throws IOException if source or destination is invalid 2904 * @throws IOException if an IO error occurs moving the file 2905 * @since 1.4 2906 */ 2907 public static void moveFile(final File srcFile, final File destFile) throws IOException { 2908 if (srcFile == null) { 2909 throw new NullPointerException("Source must not be null"); 2910 } 2911 if (destFile == null) { 2912 throw new NullPointerException("Destination must not be null"); 2913 } 2914 if (!srcFile.exists()) { 2915 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 2916 } 2917 if (srcFile.isDirectory()) { 2918 throw new IOException("Source '" + srcFile + "' is a directory"); 2919 } 2920 if (destFile.exists()) { 2921 throw new FileExistsException("Destination '" + destFile + "' already exists"); 2922 } 2923 if (destFile.isDirectory()) { 2924 throw new IOException("Destination '" + destFile + "' is a directory"); 2925 } 2926 final boolean rename = srcFile.renameTo(destFile); 2927 if (!rename) { 2928 copyFile( srcFile, destFile ); 2929 if (!srcFile.delete()) { 2930 FileUtils.deleteQuietly(destFile); 2931 throw new IOException("Failed to delete original file '" + srcFile + 2932 "' after copy to '" + destFile + "'"); 2933 } 2934 } 2935 } 2936 2937 /** 2938 * Moves a file to a directory. 2939 * 2940 * @param srcFile the file to be moved 2941 * @param destDir the destination file 2942 * @param createDestDir If {@code true} create the destination directory, 2943 * otherwise if {@code false} throw an IOException 2944 * @throws NullPointerException if source or destination is {@code null} 2945 * @throws FileExistsException if the destination file exists 2946 * @throws IOException if source or destination is invalid 2947 * @throws IOException if an IO error occurs moving the file 2948 * @since 1.4 2949 */ 2950 public static void moveFileToDirectory(final File srcFile, final File destDir, final boolean createDestDir) throws IOException { 2951 if (srcFile == null) { 2952 throw new NullPointerException("Source must not be null"); 2953 } 2954 if (destDir == null) { 2955 throw new NullPointerException("Destination directory must not be null"); 2956 } 2957 if (!destDir.exists() && createDestDir) { 2958 destDir.mkdirs(); 2959 } 2960 if (!destDir.exists()) { 2961 throw new FileNotFoundException("Destination directory '" + destDir + 2962 "' does not exist [createDestDir=" + createDestDir +"]"); 2963 } 2964 if (!destDir.isDirectory()) { 2965 throw new IOException("Destination '" + destDir + "' is not a directory"); 2966 } 2967 moveFile(srcFile, new File(destDir, srcFile.getName())); 2968 } 2969 2970 /** 2971 * Moves a file or directory to the destination directory. 2972 * <p> 2973 * When the destination is on another file system, do a "copy and delete". 2974 * 2975 * @param src the file or directory to be moved 2976 * @param destDir the destination directory 2977 * @param createDestDir If {@code true} create the destination directory, 2978 * otherwise if {@code false} throw an IOException 2979 * @throws NullPointerException if source or destination is {@code null} 2980 * @throws FileExistsException if the directory or file exists in the destination directory 2981 * @throws IOException if source or destination is invalid 2982 * @throws IOException if an IO error occurs moving the file 2983 * @since 1.4 2984 */ 2985 public static void moveToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException { 2986 if (src == null) { 2987 throw new NullPointerException("Source must not be null"); 2988 } 2989 if (destDir == null) { 2990 throw new NullPointerException("Destination must not be null"); 2991 } 2992 if (!src.exists()) { 2993 throw new FileNotFoundException("Source '" + src + "' does not exist"); 2994 } 2995 if (src.isDirectory()) { 2996 moveDirectoryToDirectory(src, destDir, createDestDir); 2997 } else { 2998 moveFileToDirectory(src, destDir, createDestDir); 2999 } 3000 } 3001 3002 /** 3003 * Determines whether the specified file is a Symbolic Link rather than an actual file. 3004 * <p> 3005 * Will not return true if there is a Symbolic Link anywhere in the path, 3006 * only if the specific file is. 3007 * <p> 3008 * <b>Note:</b> the current implementation always returns {@code false} if the system 3009 * is detected as Windows using {@link FilenameUtils#isSystemWindows()} 3010 * <p> 3011 * For code that runs on Java 1.7 or later, use the following method instead: 3012 * <br> 3013 * {@code boolean java.nio.file.Files.isSymbolicLink(Path path)} 3014 * @param file the file to check 3015 * @return true if the file is a Symbolic Link 3016 * @throws IOException if an IO error occurs while checking the file 3017 * @since 2.0 3018 */ 3019 public static boolean isSymlink(final File file) throws IOException { 3020 if (file == null) { 3021 throw new NullPointerException("File must not be null"); 3022 } 3023 if (FilenameUtils.isSystemWindows()) { 3024 return false; 3025 } 3026 File fileInCanonicalDir = null; 3027 if (file.getParent() == null) { 3028 fileInCanonicalDir = file; 3029 } else { 3030 final File canonicalDir = file.getParentFile().getCanonicalFile(); 3031 fileInCanonicalDir = new File(canonicalDir, file.getName()); 3032 } 3033 3034 if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) { 3035 return false; 3036 } else { 3037 return true; 3038 } 3039 } 3040 3041}