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 */ 017 package org.apache.commons.io; 018 019 import java.io.BufferedOutputStream; 020 import java.io.File; 021 import java.io.FileFilter; 022 import java.io.FileInputStream; 023 import java.io.FileNotFoundException; 024 import java.io.FileOutputStream; 025 import java.io.IOException; 026 import java.io.InputStream; 027 import java.io.InputStreamReader; 028 import java.io.OutputStream; 029 import java.io.Reader; 030 import java.io.UnsupportedEncodingException; 031 import java.math.BigInteger; 032 import java.net.URL; 033 import java.net.URLConnection; 034 import java.nio.ByteBuffer; 035 import java.nio.channels.FileChannel; 036 import java.nio.charset.Charset; 037 import java.nio.charset.UnsupportedCharsetException; 038 import java.util.ArrayList; 039 import java.util.Collection; 040 import java.util.Date; 041 import java.util.Iterator; 042 import java.util.List; 043 import java.util.zip.CRC32; 044 import java.util.zip.CheckedInputStream; 045 import java.util.zip.Checksum; 046 047 import org.apache.commons.io.filefilter.DirectoryFileFilter; 048 import org.apache.commons.io.filefilter.FalseFileFilter; 049 import org.apache.commons.io.filefilter.FileFilterUtils; 050 import org.apache.commons.io.filefilter.IOFileFilter; 051 import org.apache.commons.io.filefilter.SuffixFileFilter; 052 import org.apache.commons.io.filefilter.TrueFileFilter; 053 import 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 1349509 2012-06-12 20:39:23Z ggregory $ 075 */ 076 public 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(File directory, 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 (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(String... names) { 213 if (names == null) { 214 throw new NullPointerException("names must not be null"); 215 } 216 File file = null; 217 for (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(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(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(File file, 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 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(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(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(File file) throws IOException { 443 if (!file.exists()) { 444 OutputStream out = openOutputStream(file); 445 IOUtils.closeQuietly(out); 446 } 447 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(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(Collection<File> files, File directory, 477 IOFileFilter filter, boolean includeSubDirectories) { 478 File[] found = directory.listFiles((FileFilter) filter); 479 480 if (found != null) { 481 for (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 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 521 validateListFilesParameters(directory, fileFilter); 522 523 IOFileFilter effFileFilter = setUpEffectiveFileFilter(fileFilter); 524 IOFileFilter effDirFilter = setUpEffectiveDirFilter(dirFilter); 525 526 //Find files 527 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(File directory, IOFileFilter fileFilter) { 544 if (!directory.isDirectory()) { 545 throw new IllegalArgumentException("Parameter 'directory' is not a 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(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(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 subdirectories themselves. 578 * <p> 579 * @see org.apache.commons.io.FileUtils#listFiles 580 * 581 * @param directory the directory to search in 582 * @param fileFilter filter to apply when finding files. 583 * @param dirFilter optional filter to apply when finding subdirectories. 584 * If this parameter is {@code null}, subdirectories will not be included in the 585 * search. Use TrueFileFilter.INSTANCE to match all directories. 586 * @return an collection of java.io.File with the matching files 587 * @see org.apache.commons.io.filefilter.FileFilterUtils 588 * @see org.apache.commons.io.filefilter.NameFileFilter 589 * @since 2.2 590 */ 591 public static Collection<File> listFilesAndDirs( 592 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 593 validateListFilesParameters(directory, fileFilter); 594 595 IOFileFilter effFileFilter = setUpEffectiveFileFilter(fileFilter); 596 IOFileFilter effDirFilter = setUpEffectiveDirFilter(dirFilter); 597 598 //Find files 599 Collection<File> files = new java.util.LinkedList<File>(); 600 if (directory.isDirectory()) { 601 files.add(directory); 602 } 603 innerListFiles(files, directory, 604 FileFilterUtils.or(effFileFilter, effDirFilter), true); 605 return files; 606 } 607 608 /** 609 * Allows iteration over the files in given directory (and optionally 610 * its subdirectories). 611 * <p> 612 * All files found are filtered by an IOFileFilter. This method is 613 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}, 614 * which supports Iterable ('foreach' loop). 615 * <p> 616 * @param directory the directory to search in 617 * @param fileFilter filter to apply when finding files. 618 * @param dirFilter optional filter to apply when finding subdirectories. 619 * If this parameter is {@code null}, subdirectories will not be included in the 620 * search. Use TrueFileFilter.INSTANCE to match all directories. 621 * @return an iterator of java.io.File for the matching files 622 * @see org.apache.commons.io.filefilter.FileFilterUtils 623 * @see org.apache.commons.io.filefilter.NameFileFilter 624 * @since 1.2 625 */ 626 public static Iterator<File> iterateFiles( 627 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 628 return listFiles(directory, fileFilter, dirFilter).iterator(); 629 } 630 631 /** 632 * Allows iteration over the files in given directory (and optionally 633 * its subdirectories). 634 * <p> 635 * All files found are filtered by an IOFileFilter. This method is 636 * based on {@link #listFilesAndDirs(File, IOFileFilter, IOFileFilter)}, 637 * which supports Iterable ('foreach' loop). 638 * <p> 639 * The resulting iterator includes the subdirectories themselves. 640 * 641 * @param directory the directory to search in 642 * @param fileFilter filter to apply when finding files. 643 * @param dirFilter optional filter to apply when finding subdirectories. 644 * If this parameter is {@code null}, subdirectories will not be included in the 645 * search. Use TrueFileFilter.INSTANCE to match all directories. 646 * @return an iterator of java.io.File for the matching files 647 * @see org.apache.commons.io.filefilter.FileFilterUtils 648 * @see org.apache.commons.io.filefilter.NameFileFilter 649 * @since 2.2 650 */ 651 public static Iterator<File> iterateFilesAndDirs(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 652 return listFilesAndDirs(directory, fileFilter, dirFilter).iterator(); 653 } 654 655 //----------------------------------------------------------------------- 656 /** 657 * Converts an array of file extensions to suffixes for use 658 * with IOFileFilters. 659 * 660 * @param extensions an array of extensions. Format: {"java", "xml"} 661 * @return an array of suffixes. Format: {".java", ".xml"} 662 */ 663 private static String[] toSuffixes(String[] extensions) { 664 String[] suffixes = new String[extensions.length]; 665 for (int i = 0; i < extensions.length; i++) { 666 suffixes[i] = "." + extensions[i]; 667 } 668 return suffixes; 669 } 670 671 672 /** 673 * Finds files within a given directory (and optionally its subdirectories) 674 * which match an array of extensions. 675 * 676 * @param directory the directory to search in 677 * @param extensions an array of extensions, ex. {"java","xml"}. If this 678 * parameter is {@code null}, all files are returned. 679 * @param recursive if true all subdirectories are searched as well 680 * @return an collection of java.io.File with the matching files 681 */ 682 public static Collection<File> listFiles( 683 File directory, String[] extensions, boolean recursive) { 684 IOFileFilter filter; 685 if (extensions == null) { 686 filter = TrueFileFilter.INSTANCE; 687 } else { 688 String[] suffixes = toSuffixes(extensions); 689 filter = new SuffixFileFilter(suffixes); 690 } 691 return listFiles(directory, filter, 692 recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE); 693 } 694 695 /** 696 * Allows iteration over the files in a given directory (and optionally 697 * its subdirectories) which match an array of extensions. This method 698 * is based on {@link #listFiles(File, String[], boolean)}, 699 * which supports Iterable ('foreach' loop). 700 * 701 * @param directory the directory to search in 702 * @param extensions an array of extensions, ex. {"java","xml"}. If this 703 * parameter is {@code null}, all files are returned. 704 * @param recursive if true all subdirectories are searched as well 705 * @return an iterator of java.io.File with the matching files 706 * @since 1.2 707 */ 708 public static Iterator<File> iterateFiles( 709 File directory, String[] extensions, boolean recursive) { 710 return listFiles(directory, extensions, recursive).iterator(); 711 } 712 713 //----------------------------------------------------------------------- 714 /** 715 * Compares the contents of two files to determine if they are equal or not. 716 * <p> 717 * This method checks to see if the two files are different lengths 718 * or if they point to the same file, before resorting to byte-by-byte 719 * comparison of the contents. 720 * <p> 721 * Code origin: Avalon 722 * 723 * @param file1 the first file 724 * @param file2 the second file 725 * @return true if the content of the files are equal or they both don't 726 * exist, false otherwise 727 * @throws IOException in case of an I/O error 728 */ 729 public static boolean contentEquals(File file1, File file2) throws IOException { 730 boolean file1Exists = file1.exists(); 731 if (file1Exists != file2.exists()) { 732 return false; 733 } 734 735 if (!file1Exists) { 736 // two not existing files are equal 737 return true; 738 } 739 740 if (file1.isDirectory() || file2.isDirectory()) { 741 // don't want to compare directory contents 742 throw new IOException("Can't compare directories, only files"); 743 } 744 745 if (file1.length() != file2.length()) { 746 // lengths differ, cannot be equal 747 return false; 748 } 749 750 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 751 // same file 752 return true; 753 } 754 755 InputStream input1 = null; 756 InputStream input2 = null; 757 try { 758 input1 = new FileInputStream(file1); 759 input2 = new FileInputStream(file2); 760 return IOUtils.contentEquals(input1, input2); 761 762 } finally { 763 IOUtils.closeQuietly(input1); 764 IOUtils.closeQuietly(input2); 765 } 766 } 767 768 //----------------------------------------------------------------------- 769 /** 770 * Compares the contents of two files to determine if they are equal or not. 771 * <p> 772 * This method checks to see if the two files point to the same file, 773 * before resorting to line-by-line comparison of the contents. 774 * <p> 775 * 776 * @param file1 the first file 777 * @param file2 the second file 778 * @param charsetName the character encoding to be used. 779 * May be null, in which case the platform default is used 780 * @return true if the content of the files are equal or neither exists, 781 * false otherwise 782 * @throws IOException in case of an I/O error 783 * @since 2.2 784 * @see IOUtils#contentEqualsIgnoreEOL(Reader, Reader) 785 */ 786 public static boolean contentEqualsIgnoreEOL(File file1, File file2, String charsetName) throws IOException { 787 boolean file1Exists = file1.exists(); 788 if (file1Exists != file2.exists()) { 789 return false; 790 } 791 792 if (!file1Exists) { 793 // two not existing files are equal 794 return true; 795 } 796 797 if (file1.isDirectory() || file2.isDirectory()) { 798 // don't want to compare directory contents 799 throw new IOException("Can't compare directories, only files"); 800 } 801 802 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 803 // same file 804 return true; 805 } 806 807 Reader input1 = null; 808 Reader input2 = null; 809 try { 810 if (charsetName == null) { 811 input1 = new InputStreamReader(new FileInputStream(file1)); 812 input2 = new InputStreamReader(new FileInputStream(file2)); 813 } else { 814 input1 = new InputStreamReader(new FileInputStream(file1), charsetName); 815 input2 = new InputStreamReader(new FileInputStream(file2), charsetName); 816 } 817 return IOUtils.contentEqualsIgnoreEOL(input1, input2); 818 819 } finally { 820 IOUtils.closeQuietly(input1); 821 IOUtils.closeQuietly(input2); 822 } 823 } 824 825 //----------------------------------------------------------------------- 826 /** 827 * Convert from a <code>URL</code> to a <code>File</code>. 828 * <p> 829 * From version 1.1 this method will decode the URL. 830 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 831 * correctly decoded to <code>/my docs/file.txt</code>. Starting with version 832 * 1.5, this method uses UTF-8 to decode percent-encoded octets to characters. 833 * Additionally, malformed percent-encoded octets are handled leniently by 834 * passing them through literally. 835 * 836 * @param url the file URL to convert, {@code null} returns {@code null} 837 * @return the equivalent <code>File</code> object, or {@code null} 838 * if the URL's protocol is not <code>file</code> 839 */ 840 public static File toFile(URL url) { 841 if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) { 842 return null; 843 } else { 844 String filename = url.getFile().replace('/', File.separatorChar); 845 filename = decodeUrl(filename); 846 return new File(filename); 847 } 848 } 849 850 /** 851 * Decodes the specified URL as per RFC 3986, i.e. transforms 852 * percent-encoded octets to characters by decoding with the UTF-8 character 853 * set. This function is primarily intended for usage with 854 * {@link java.net.URL} which unfortunately does not enforce proper URLs. As 855 * such, this method will leniently accept invalid characters or malformed 856 * percent-encoded octets and simply pass them literally through to the 857 * result string. Except for rare edge cases, this will make unencoded URLs 858 * pass through unaltered. 859 * 860 * @param url The URL to decode, may be {@code null}. 861 * @return The decoded URL or {@code null} if the input was 862 * {@code null}. 863 */ 864 static String decodeUrl(String url) { 865 String decoded = url; 866 if (url != null && url.indexOf('%') >= 0) { 867 int n = url.length(); 868 StringBuffer buffer = new StringBuffer(); 869 ByteBuffer bytes = ByteBuffer.allocate(n); 870 for (int i = 0; i < n;) { 871 if (url.charAt(i) == '%') { 872 try { 873 do { 874 byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16); 875 bytes.put(octet); 876 i += 3; 877 } while (i < n && url.charAt(i) == '%'); 878 continue; 879 } catch (RuntimeException e) { 880 // malformed percent-encoded octet, fall through and 881 // append characters literally 882 } finally { 883 if (bytes.position() > 0) { 884 bytes.flip(); 885 buffer.append(UTF8.decode(bytes).toString()); 886 bytes.clear(); 887 } 888 } 889 } 890 buffer.append(url.charAt(i++)); 891 } 892 decoded = buffer.toString(); 893 } 894 return decoded; 895 } 896 897 /** 898 * Converts each of an array of <code>URL</code> to a <code>File</code>. 899 * <p> 900 * Returns an array of the same size as the input. 901 * If the input is {@code null}, an empty array is returned. 902 * If the input contains {@code null}, the output array contains {@code null} at the same 903 * index. 904 * <p> 905 * This method will decode the URL. 906 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 907 * correctly decoded to <code>/my docs/file.txt</code>. 908 * 909 * @param urls the file URLs to convert, {@code null} returns empty array 910 * @return a non-{@code null} array of Files matching the input, with a {@code null} item 911 * if there was a {@code null} at that index in the input array 912 * @throws IllegalArgumentException if any file is not a URL file 913 * @throws IllegalArgumentException if any file is incorrectly encoded 914 * @since 1.1 915 */ 916 public static File[] toFiles(URL[] urls) { 917 if (urls == null || urls.length == 0) { 918 return EMPTY_FILE_ARRAY; 919 } 920 File[] files = new File[urls.length]; 921 for (int i = 0; i < urls.length; i++) { 922 URL url = urls[i]; 923 if (url != null) { 924 if (url.getProtocol().equals("file") == false) { 925 throw new IllegalArgumentException( 926 "URL could not be converted to a File: " + url); 927 } 928 files[i] = toFile(url); 929 } 930 } 931 return files; 932 } 933 934 /** 935 * Converts each of an array of <code>File</code> to a <code>URL</code>. 936 * <p> 937 * Returns an array of the same size as the input. 938 * 939 * @param files the files to convert, must not be {@code null} 940 * @return an array of URLs matching the input 941 * @throws IOException if a file cannot be converted 942 * @throws NullPointerException if the parameter is null 943 */ 944 public static URL[] toURLs(File[] files) throws IOException { 945 URL[] urls = new URL[files.length]; 946 947 for (int i = 0; i < urls.length; i++) { 948 urls[i] = files[i].toURI().toURL(); 949 } 950 951 return urls; 952 } 953 954 //----------------------------------------------------------------------- 955 /** 956 * Copies a file to a directory preserving the file date. 957 * <p> 958 * This method copies the contents of the specified source file 959 * to a file of the same name in the specified destination directory. 960 * The destination directory is created if it does not exist. 961 * If the destination file exists, then this method will overwrite it. 962 * <p> 963 * <strong>Note:</strong> This method tries to preserve the file's last 964 * modified date/times using {@link File#setLastModified(long)}, however 965 * it is not guaranteed that the operation will succeed. 966 * If the modification operation fails, no indication is provided. 967 * 968 * @param srcFile an existing file to copy, must not be {@code null} 969 * @param destDir the directory to place the copy in, must not be {@code null} 970 * 971 * @throws NullPointerException if source or destination is null 972 * @throws IOException if source or destination is invalid 973 * @throws IOException if an IO error occurs during copying 974 * @see #copyFile(File, File, boolean) 975 */ 976 public static void copyFileToDirectory(File srcFile, File destDir) throws IOException { 977 copyFileToDirectory(srcFile, destDir, true); 978 } 979 980 /** 981 * Copies a file to a directory optionally preserving the file date. 982 * <p> 983 * This method copies the contents of the specified source file 984 * to a file of the same name in the specified destination directory. 985 * The destination directory is created if it does not exist. 986 * If the destination file exists, then this method will overwrite it. 987 * <p> 988 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 989 * {@code true} tries to preserve the file's last modified 990 * date/times using {@link File#setLastModified(long)}, however it is 991 * not guaranteed that the operation will succeed. 992 * If the modification operation fails, no indication is provided. 993 * 994 * @param srcFile an existing file to copy, must not be {@code null} 995 * @param destDir the directory to place the copy in, must not be {@code null} 996 * @param preserveFileDate true if the file date of the copy 997 * should be the same as the original 998 * 999 * @throws NullPointerException if source or destination is {@code null} 1000 * @throws IOException if source or destination is invalid 1001 * @throws IOException if an IO error occurs during copying 1002 * @see #copyFile(File, File, boolean) 1003 * @since 1.3 1004 */ 1005 public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException { 1006 if (destDir == null) { 1007 throw new NullPointerException("Destination must not be null"); 1008 } 1009 if (destDir.exists() && destDir.isDirectory() == false) { 1010 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 1011 } 1012 File destFile = new File(destDir, srcFile.getName()); 1013 copyFile(srcFile, destFile, preserveFileDate); 1014 } 1015 1016 /** 1017 * Copies a file to a new location preserving the file date. 1018 * <p> 1019 * This method copies the contents of the specified source file to the 1020 * specified destination file. The directory holding the destination file is 1021 * created if it does not exist. If the destination file exists, then this 1022 * method will overwrite it. 1023 * <p> 1024 * <strong>Note:</strong> This method tries to preserve the file's last 1025 * modified date/times using {@link File#setLastModified(long)}, however 1026 * it is not guaranteed that the operation will succeed. 1027 * If the modification operation fails, no indication is provided. 1028 * 1029 * @param srcFile an existing file to copy, must not be {@code null} 1030 * @param destFile the new file, must not be {@code null} 1031 * 1032 * @throws NullPointerException if source or destination is {@code null} 1033 * @throws IOException if source or destination is invalid 1034 * @throws IOException if an IO error occurs during copying 1035 * @see #copyFileToDirectory(File, File) 1036 */ 1037 public static void copyFile(File srcFile, File destFile) throws IOException { 1038 copyFile(srcFile, destFile, true); 1039 } 1040 1041 /** 1042 * Copies a file to a new location. 1043 * <p> 1044 * This method copies the contents of the specified source file 1045 * to the specified destination file. 1046 * The directory holding the destination file is created if it does not exist. 1047 * If the destination file exists, then this method will overwrite it. 1048 * <p> 1049 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1050 * {@code true} tries to preserve the file's last modified 1051 * date/times using {@link File#setLastModified(long)}, however it is 1052 * not guaranteed that the operation will succeed. 1053 * If the modification operation fails, no indication is provided. 1054 * 1055 * @param srcFile an existing file to copy, must not be {@code null} 1056 * @param destFile the new file, must not be {@code null} 1057 * @param preserveFileDate true if the file date of the copy 1058 * should be the same as the original 1059 * 1060 * @throws NullPointerException if source or destination is {@code null} 1061 * @throws IOException if source or destination is invalid 1062 * @throws IOException if an IO error occurs during copying 1063 * @see #copyFileToDirectory(File, File, boolean) 1064 */ 1065 public static void copyFile(File srcFile, File destFile, 1066 boolean preserveFileDate) throws IOException { 1067 if (srcFile == null) { 1068 throw new NullPointerException("Source must not be null"); 1069 } 1070 if (destFile == null) { 1071 throw new NullPointerException("Destination must not be null"); 1072 } 1073 if (srcFile.exists() == false) { 1074 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 1075 } 1076 if (srcFile.isDirectory()) { 1077 throw new IOException("Source '" + srcFile + "' exists but is a directory"); 1078 } 1079 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { 1080 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); 1081 } 1082 File parentFile = destFile.getParentFile(); 1083 if (parentFile != null) { 1084 if (!parentFile.mkdirs() && !parentFile.isDirectory()) { 1085 throw new IOException("Destination '" + parentFile + "' directory cannot be created"); 1086 } 1087 } 1088 if (destFile.exists() && destFile.canWrite() == false) { 1089 throw new IOException("Destination '" + destFile + "' exists but is read-only"); 1090 } 1091 doCopyFile(srcFile, destFile, preserveFileDate); 1092 } 1093 1094 /** 1095 * Copy bytes from a <code>File</code> to an <code>OutputStream</code>. 1096 * <p> 1097 * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>. 1098 * </p> 1099 * 1100 * @param input 1101 * the <code>File</code> to read from 1102 * @param output 1103 * the <code>OutputStream</code> to write to 1104 * @return the number of bytes copied 1105 * @throws NullPointerException 1106 * if the input or output is null 1107 * @throws IOException 1108 * if an I/O error occurs 1109 * @since 2.1 1110 */ 1111 public static long copyFile(File input, OutputStream output) throws IOException { 1112 final FileInputStream fis = new FileInputStream(input); 1113 try { 1114 return IOUtils.copyLarge(fis, output); 1115 } finally { 1116 fis.close(); 1117 } 1118 } 1119 1120 /** 1121 * Internal copy file method. 1122 * 1123 * @param srcFile the validated source file, must not be {@code null} 1124 * @param destFile the validated destination file, must not be {@code null} 1125 * @param preserveFileDate whether to preserve the file date 1126 * @throws IOException if an error occurs 1127 */ 1128 private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException { 1129 if (destFile.exists() && destFile.isDirectory()) { 1130 throw new IOException("Destination '" + destFile + "' exists but is a directory"); 1131 } 1132 1133 FileInputStream fis = null; 1134 FileOutputStream fos = null; 1135 FileChannel input = null; 1136 FileChannel output = null; 1137 try { 1138 fis = new FileInputStream(srcFile); 1139 fos = new FileOutputStream(destFile); 1140 input = fis.getChannel(); 1141 output = fos.getChannel(); 1142 long size = input.size(); 1143 long pos = 0; 1144 long count = 0; 1145 while (pos < size) { 1146 count = size - pos > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : size - pos; 1147 pos += output.transferFrom(input, pos, count); 1148 } 1149 } finally { 1150 IOUtils.closeQuietly(output); 1151 IOUtils.closeQuietly(fos); 1152 IOUtils.closeQuietly(input); 1153 IOUtils.closeQuietly(fis); 1154 } 1155 1156 if (srcFile.length() != destFile.length()) { 1157 throw new IOException("Failed to copy full contents from '" + 1158 srcFile + "' to '" + destFile + "'"); 1159 } 1160 if (preserveFileDate) { 1161 destFile.setLastModified(srcFile.lastModified()); 1162 } 1163 } 1164 1165 //----------------------------------------------------------------------- 1166 /** 1167 * Copies a directory to within another directory preserving the file dates. 1168 * <p> 1169 * This method copies the source directory and all its contents to a 1170 * directory of the same name in the specified destination directory. 1171 * <p> 1172 * The destination directory is created if it does not exist. 1173 * If the destination directory did exist, then this method merges 1174 * the source with the destination, with the source taking precedence. 1175 * <p> 1176 * <strong>Note:</strong> This method tries to preserve the files' last 1177 * modified date/times using {@link File#setLastModified(long)}, however 1178 * it is not guaranteed that those operations will succeed. 1179 * If the modification operation fails, no indication is provided. 1180 * 1181 * @param srcDir an existing directory to copy, must not be {@code null} 1182 * @param destDir the directory to place the copy in, must not be {@code null} 1183 * 1184 * @throws NullPointerException if source or destination is {@code null} 1185 * @throws IOException if source or destination is invalid 1186 * @throws IOException if an IO error occurs during copying 1187 * @since 1.2 1188 */ 1189 public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException { 1190 if (srcDir == null) { 1191 throw new NullPointerException("Source must not be null"); 1192 } 1193 if (srcDir.exists() && srcDir.isDirectory() == false) { 1194 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory"); 1195 } 1196 if (destDir == null) { 1197 throw new NullPointerException("Destination must not be null"); 1198 } 1199 if (destDir.exists() && destDir.isDirectory() == false) { 1200 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 1201 } 1202 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true); 1203 } 1204 1205 /** 1206 * Copies a whole directory to a new location preserving the file dates. 1207 * <p> 1208 * This method copies the specified directory and all its child 1209 * directories and files to the specified destination. 1210 * The destination is the new location and name of the directory. 1211 * <p> 1212 * The destination directory is created if it does not exist. 1213 * If the destination directory did exist, then this method merges 1214 * the source with the destination, with the source taking precedence. 1215 * <p> 1216 * <strong>Note:</strong> This method tries to preserve the files' last 1217 * modified date/times using {@link File#setLastModified(long)}, however 1218 * it is not guaranteed that those operations will succeed. 1219 * If the modification operation fails, no indication is provided. 1220 * 1221 * @param srcDir an existing directory to copy, must not be {@code null} 1222 * @param destDir the new directory, must not be {@code null} 1223 * 1224 * @throws NullPointerException if source or destination is {@code null} 1225 * @throws IOException if source or destination is invalid 1226 * @throws IOException if an IO error occurs during copying 1227 * @since 1.1 1228 */ 1229 public static void copyDirectory(File srcDir, File destDir) throws IOException { 1230 copyDirectory(srcDir, destDir, true); 1231 } 1232 1233 /** 1234 * Copies a whole directory to a new location. 1235 * <p> 1236 * This method copies the contents of the specified source directory 1237 * to within the specified destination directory. 1238 * <p> 1239 * The destination directory is created if it does not exist. 1240 * If the destination directory did exist, then this method merges 1241 * the source with the destination, with the source taking precedence. 1242 * <p> 1243 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1244 * {@code true} tries to preserve the files' last modified 1245 * date/times using {@link File#setLastModified(long)}, however it is 1246 * not guaranteed that those operations will succeed. 1247 * If the modification operation fails, no indication is provided. 1248 * 1249 * @param srcDir an existing directory to copy, must not be {@code null} 1250 * @param destDir the new directory, must not be {@code null} 1251 * @param preserveFileDate true if the file date of the copy 1252 * should be the same as the original 1253 * 1254 * @throws NullPointerException if source or destination is {@code null} 1255 * @throws IOException if source or destination is invalid 1256 * @throws IOException if an IO error occurs during copying 1257 * @since 1.1 1258 */ 1259 public static void copyDirectory(File srcDir, File destDir, 1260 boolean preserveFileDate) throws IOException { 1261 copyDirectory(srcDir, destDir, null, preserveFileDate); 1262 } 1263 1264 /** 1265 * Copies a filtered directory to a new location preserving the file dates. 1266 * <p> 1267 * This method copies the contents of the specified source directory 1268 * to within the specified destination directory. 1269 * <p> 1270 * The destination directory is created if it does not exist. 1271 * If the destination directory did exist, then this method merges 1272 * the source with the destination, with the source taking precedence. 1273 * <p> 1274 * <strong>Note:</strong> This method tries to preserve the files' last 1275 * modified date/times using {@link File#setLastModified(long)}, however 1276 * it is not guaranteed that those operations will succeed. 1277 * If the modification operation fails, no indication is provided. 1278 * 1279 * <h4>Example: Copy directories only</h4> 1280 * <pre> 1281 * // only copy the directory structure 1282 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY); 1283 * </pre> 1284 * 1285 * <h4>Example: Copy directories and txt files</h4> 1286 * <pre> 1287 * // Create a filter for ".txt" files 1288 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 1289 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 1290 * 1291 * // Create a filter for either directories or ".txt" files 1292 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 1293 * 1294 * // Copy using the filter 1295 * FileUtils.copyDirectory(srcDir, destDir, filter); 1296 * </pre> 1297 * 1298 * @param srcDir an existing directory to copy, must not be {@code null} 1299 * @param destDir the new directory, must not be {@code null} 1300 * @param filter the filter to apply, null means copy all directories and files 1301 * should be the same as the original 1302 * 1303 * @throws NullPointerException if source or destination is {@code null} 1304 * @throws IOException if source or destination is invalid 1305 * @throws IOException if an IO error occurs during copying 1306 * @since 1.4 1307 */ 1308 public static void copyDirectory(File srcDir, File destDir, 1309 FileFilter filter) throws IOException { 1310 copyDirectory(srcDir, destDir, filter, true); 1311 } 1312 1313 /** 1314 * Copies a filtered directory to a new location. 1315 * <p> 1316 * This method copies the contents of the specified source directory 1317 * to within the specified destination directory. 1318 * <p> 1319 * The destination directory is created if it does not exist. 1320 * If the destination directory did exist, then this method merges 1321 * the source with the destination, with the source taking precedence. 1322 * <p> 1323 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1324 * {@code true} tries to preserve the files' last modified 1325 * date/times using {@link File#setLastModified(long)}, however it is 1326 * not guaranteed that those operations will succeed. 1327 * If the modification operation fails, no indication is provided. 1328 * 1329 * <h4>Example: Copy directories only</h4> 1330 * <pre> 1331 * // only copy the directory structure 1332 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false); 1333 * </pre> 1334 * 1335 * <h4>Example: Copy directories and txt files</h4> 1336 * <pre> 1337 * // Create a filter for ".txt" files 1338 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 1339 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 1340 * 1341 * // Create a filter for either directories or ".txt" files 1342 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 1343 * 1344 * // Copy using the filter 1345 * FileUtils.copyDirectory(srcDir, destDir, filter, false); 1346 * </pre> 1347 * 1348 * @param srcDir an existing directory to copy, must not be {@code null} 1349 * @param destDir the new directory, must not be {@code null} 1350 * @param filter the filter to apply, null means copy all directories and files 1351 * @param preserveFileDate true if the file date of the copy 1352 * should be the same as the original 1353 * 1354 * @throws NullPointerException if source or destination is {@code null} 1355 * @throws IOException if source or destination is invalid 1356 * @throws IOException if an IO error occurs during copying 1357 * @since 1.4 1358 */ 1359 public static void copyDirectory(File srcDir, File destDir, 1360 FileFilter filter, boolean preserveFileDate) throws IOException { 1361 if (srcDir == null) { 1362 throw new NullPointerException("Source must not be null"); 1363 } 1364 if (destDir == null) { 1365 throw new NullPointerException("Destination must not be null"); 1366 } 1367 if (srcDir.exists() == false) { 1368 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 1369 } 1370 if (srcDir.isDirectory() == false) { 1371 throw new IOException("Source '" + srcDir + "' exists but is not a directory"); 1372 } 1373 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) { 1374 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); 1375 } 1376 1377 // Cater for destination being directory within the source directory (see IO-141) 1378 List<String> exclusionList = null; 1379 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) { 1380 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 1381 if (srcFiles != null && srcFiles.length > 0) { 1382 exclusionList = new ArrayList<String>(srcFiles.length); 1383 for (File srcFile : srcFiles) { 1384 File copiedFile = new File(destDir, srcFile.getName()); 1385 exclusionList.add(copiedFile.getCanonicalPath()); 1386 } 1387 } 1388 } 1389 doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList); 1390 } 1391 1392 /** 1393 * Internal copy directory method. 1394 * 1395 * @param srcDir the validated source directory, must not be {@code null} 1396 * @param destDir the validated destination directory, must not be {@code null} 1397 * @param filter the filter to apply, null means copy all directories and files 1398 * @param preserveFileDate whether to preserve the file date 1399 * @param exclusionList List of files and directories to exclude from the copy, may be null 1400 * @throws IOException if an error occurs 1401 * @since 1.1 1402 */ 1403 private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter, 1404 boolean preserveFileDate, List<String> exclusionList) throws IOException { 1405 // recurse 1406 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 1407 if (srcFiles == null) { // null if abstract pathname does not denote a directory, or if an I/O error occurs 1408 throw new IOException("Failed to list contents of " + srcDir); 1409 } 1410 if (destDir.exists()) { 1411 if (destDir.isDirectory() == false) { 1412 throw new IOException("Destination '" + destDir + "' exists but is not a directory"); 1413 } 1414 } else { 1415 if (!destDir.mkdirs() && !destDir.isDirectory()) { 1416 throw new IOException("Destination '" + destDir + "' directory cannot be created"); 1417 } 1418 } 1419 if (destDir.canWrite() == false) { 1420 throw new IOException("Destination '" + destDir + "' cannot be written to"); 1421 } 1422 for (File srcFile : srcFiles) { 1423 File dstFile = new File(destDir, srcFile.getName()); 1424 if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) { 1425 if (srcFile.isDirectory()) { 1426 doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList); 1427 } else { 1428 doCopyFile(srcFile, dstFile, preserveFileDate); 1429 } 1430 } 1431 } 1432 1433 // Do this last, as the above has probably affected directory metadata 1434 if (preserveFileDate) { 1435 destDir.setLastModified(srcDir.lastModified()); 1436 } 1437 } 1438 1439 //----------------------------------------------------------------------- 1440 /** 1441 * Copies bytes from the URL <code>source</code> to a file 1442 * <code>destination</code>. The directories up to <code>destination</code> 1443 * will be created if they don't already exist. <code>destination</code> 1444 * will be overwritten if it already exists. 1445 * <p> 1446 * Warning: this method does not set a connection or read timeout and thus 1447 * might block forever. Use {@link #copyURLToFile(URL, File, int, int)} 1448 * with reasonable timeouts to prevent this. 1449 * 1450 * @param source the <code>URL</code> to copy bytes from, must not be {@code null} 1451 * @param destination the non-directory <code>File</code> to write bytes to 1452 * (possibly overwriting), must not be {@code null} 1453 * @throws IOException if <code>source</code> URL cannot be opened 1454 * @throws IOException if <code>destination</code> is a directory 1455 * @throws IOException if <code>destination</code> cannot be written 1456 * @throws IOException if <code>destination</code> needs creating but can't be 1457 * @throws IOException if an IO error occurs during copying 1458 */ 1459 public static void copyURLToFile(URL source, File destination) throws IOException { 1460 InputStream input = source.openStream(); 1461 copyInputStreamToFile(input, destination); 1462 } 1463 1464 /** 1465 * Copies bytes from the URL <code>source</code> to a file 1466 * <code>destination</code>. The directories up to <code>destination</code> 1467 * will be created if they don't already exist. <code>destination</code> 1468 * will be overwritten if it already exists. 1469 * 1470 * @param source the <code>URL</code> to copy bytes from, must not be {@code null} 1471 * @param destination the non-directory <code>File</code> to write bytes to 1472 * (possibly overwriting), must not be {@code null} 1473 * @param connectionTimeout the number of milliseconds until this method 1474 * will timeout if no connection could be established to the <code>source</code> 1475 * @param readTimeout the number of milliseconds until this method will 1476 * timeout if no data could be read from the <code>source</code> 1477 * @throws IOException if <code>source</code> URL cannot be opened 1478 * @throws IOException if <code>destination</code> is a directory 1479 * @throws IOException if <code>destination</code> cannot be written 1480 * @throws IOException if <code>destination</code> needs creating but can't be 1481 * @throws IOException if an IO error occurs during copying 1482 * @since 2.0 1483 */ 1484 public static void copyURLToFile(URL source, File destination, 1485 int connectionTimeout, int readTimeout) throws IOException { 1486 URLConnection connection = source.openConnection(); 1487 connection.setConnectTimeout(connectionTimeout); 1488 connection.setReadTimeout(readTimeout); 1489 InputStream input = connection.getInputStream(); 1490 copyInputStreamToFile(input, 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 * 1499 * @param source the <code>InputStream</code> to copy bytes from, must not be {@code null} 1500 * @param destination the non-directory <code>File</code> to write bytes to 1501 * (possibly overwriting), must not be {@code null} 1502 * @throws IOException if <code>destination</code> is a directory 1503 * @throws IOException if <code>destination</code> cannot be written 1504 * @throws IOException if <code>destination</code> needs creating but can't be 1505 * @throws IOException if an IO error occurs during copying 1506 * @since 2.0 1507 */ 1508 public static void copyInputStreamToFile(InputStream source, File destination) throws IOException { 1509 try { 1510 FileOutputStream output = openOutputStream(destination); 1511 try { 1512 IOUtils.copy(source, output); 1513 output.close(); // don't swallow close Exception if copy completes normally 1514 } finally { 1515 IOUtils.closeQuietly(output); 1516 } 1517 } finally { 1518 IOUtils.closeQuietly(source); 1519 } 1520 } 1521 1522 //----------------------------------------------------------------------- 1523 /** 1524 * Deletes a directory recursively. 1525 * 1526 * @param directory directory to delete 1527 * @throws IOException in case deletion is unsuccessful 1528 */ 1529 public static void deleteDirectory(File directory) throws IOException { 1530 if (!directory.exists()) { 1531 return; 1532 } 1533 1534 if (!isSymlink(directory)) { 1535 cleanDirectory(directory); 1536 } 1537 1538 if (!directory.delete()) { 1539 String message = 1540 "Unable to delete directory " + directory + "."; 1541 throw new IOException(message); 1542 } 1543 } 1544 1545 /** 1546 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. 1547 * <p> 1548 * The difference between File.delete() and this method are: 1549 * <ul> 1550 * <li>A directory to be deleted does not have to be empty.</li> 1551 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li> 1552 * </ul> 1553 * 1554 * @param file file or directory to delete, can be {@code null} 1555 * @return {@code true} if the file or directory was deleted, otherwise 1556 * {@code false} 1557 * 1558 * @since 1.4 1559 */ 1560 public static boolean deleteQuietly(File file) { 1561 if (file == null) { 1562 return false; 1563 } 1564 try { 1565 if (file.isDirectory()) { 1566 cleanDirectory(file); 1567 } 1568 } catch (Exception ignored) { 1569 } 1570 1571 try { 1572 return file.delete(); 1573 } catch (Exception ignored) { 1574 return false; 1575 } 1576 } 1577 1578 /** 1579 * Determines whether the {@code parent} directory contains the {@code child} element (a file or directory). 1580 * <p> 1581 * Files are normalized before comparison. 1582 * </p> 1583 * 1584 * Edge cases: 1585 * <ul> 1586 * <li>A {@code directory} must not be null: if null, throw IllegalArgumentException</li> 1587 * <li>A {@code directory} must be a directory: if not a directory, throw IllegalArgumentException</li> 1588 * <li>A directory does not contain itself: return false</li> 1589 * <li>A null child file is not contained in any parent: return false</li> 1590 * </ul> 1591 * 1592 * @param directory 1593 * the file to consider as the parent. 1594 * @param child 1595 * the file to consider as the child. 1596 * @return true is the candidate leaf is under by the specified composite. False otherwise. 1597 * @throws IOException 1598 * if an IO error occurs while checking the files. 1599 * @since 2.2 1600 * @see FilenameUtils#directoryContains(String, String) 1601 */ 1602 public static boolean directoryContains(final File directory, final File child) throws IOException { 1603 1604 // Fail fast against NullPointerException 1605 if (directory == null) { 1606 throw new IllegalArgumentException("Directory must not be null"); 1607 } 1608 1609 if (!directory.isDirectory()) { 1610 throw new IllegalArgumentException("Not a directory: " + directory); 1611 } 1612 1613 if (child == null) { 1614 return false; 1615 } 1616 1617 if (!directory.exists() || !child.exists()) { 1618 return false; 1619 } 1620 1621 // Canonicalize paths (normalizes relative paths) 1622 String canonicalParent = directory.getCanonicalPath(); 1623 String canonicalChild = child.getCanonicalPath(); 1624 1625 return FilenameUtils.directoryContains(canonicalParent, canonicalChild); 1626 } 1627 1628 /** 1629 * Cleans a directory without deleting it. 1630 * 1631 * @param directory directory to clean 1632 * @throws IOException in case cleaning is unsuccessful 1633 */ 1634 public static void cleanDirectory(File directory) throws IOException { 1635 if (!directory.exists()) { 1636 String message = directory + " does not exist"; 1637 throw new IllegalArgumentException(message); 1638 } 1639 1640 if (!directory.isDirectory()) { 1641 String message = directory + " is not a directory"; 1642 throw new IllegalArgumentException(message); 1643 } 1644 1645 File[] files = directory.listFiles(); 1646 if (files == null) { // null if security restricted 1647 throw new IOException("Failed to list contents of " + directory); 1648 } 1649 1650 IOException exception = null; 1651 for (File file : files) { 1652 try { 1653 forceDelete(file); 1654 } catch (IOException ioe) { 1655 exception = ioe; 1656 } 1657 } 1658 1659 if (null != exception) { 1660 throw exception; 1661 } 1662 } 1663 1664 //----------------------------------------------------------------------- 1665 /** 1666 * Waits for NFS to propagate a file creation, imposing a timeout. 1667 * <p> 1668 * This method repeatedly tests {@link File#exists()} until it returns 1669 * true up to the maximum time specified in seconds. 1670 * 1671 * @param file the file to check, must not be {@code null} 1672 * @param seconds the maximum time in seconds to wait 1673 * @return true if file exists 1674 * @throws NullPointerException if the file is {@code null} 1675 */ 1676 public static boolean waitFor(File file, int seconds) { 1677 int timeout = 0; 1678 int tick = 0; 1679 while (!file.exists()) { 1680 if (tick++ >= 10) { 1681 tick = 0; 1682 if (timeout++ > seconds) { 1683 return false; 1684 } 1685 } 1686 try { 1687 Thread.sleep(100); 1688 } catch (InterruptedException ignore) { 1689 // ignore exception 1690 } catch (Exception ex) { 1691 break; 1692 } 1693 } 1694 return true; 1695 } 1696 1697 //----------------------------------------------------------------------- 1698 /** 1699 * Reads the contents of a file into a String. 1700 * The file is always closed. 1701 * 1702 * @param file the file to read, must not be {@code null} 1703 * @param encoding the encoding to use, {@code null} means platform default 1704 * @return the file contents, never {@code null} 1705 * @throws IOException in case of an I/O error 1706 * @since 2.3 1707 */ 1708 public static String readFileToString(File file, Charset encoding) throws IOException { 1709 InputStream in = null; 1710 try { 1711 in = openInputStream(file); 1712 return IOUtils.toString(in, Charsets.toCharset(encoding)); 1713 } finally { 1714 IOUtils.closeQuietly(in); 1715 } 1716 } 1717 1718 /** 1719 * Reads the contents of a file into a String. The file is always closed. 1720 * 1721 * @param file 1722 * the file to read, must not be {@code null} 1723 * @param encoding 1724 * the encoding to use, {@code null} means platform default 1725 * @return the file contents, never {@code null} 1726 * @throws IOException 1727 * in case of an I/O error 1728 * @throws UnsupportedCharsetException 1729 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 1730 * supported. 1731 * @since 2.3 1732 */ 1733 public static String readFileToString(File file, String encoding) throws IOException { 1734 return readFileToString(file, Charsets.toCharset(encoding)); 1735 } 1736 1737 1738 /** 1739 * Reads the contents of a file into a String using the default encoding for the VM. 1740 * The file is always closed. 1741 * 1742 * @param file the file to read, must not be {@code null} 1743 * @return the file contents, never {@code null} 1744 * @throws IOException in case of an I/O error 1745 * @since 1.3.1 1746 */ 1747 public static String readFileToString(File file) throws IOException { 1748 return readFileToString(file, Charset.defaultCharset()); 1749 } 1750 1751 /** 1752 * Reads the contents of a file into a byte array. 1753 * The file is always closed. 1754 * 1755 * @param file the file to read, must not be {@code null} 1756 * @return the file contents, never {@code null} 1757 * @throws IOException in case of an I/O error 1758 * @since 1.1 1759 */ 1760 public static byte[] readFileToByteArray(File file) throws IOException { 1761 InputStream in = null; 1762 try { 1763 in = openInputStream(file); 1764 return IOUtils.toByteArray(in, file.length()); 1765 } finally { 1766 IOUtils.closeQuietly(in); 1767 } 1768 } 1769 1770 /** 1771 * Reads the contents of a file line by line to a List of Strings. 1772 * The file is always closed. 1773 * 1774 * @param file the file to read, must not be {@code null} 1775 * @param encoding the encoding to use, {@code null} means platform default 1776 * @return the list of Strings representing each line in the file, never {@code null} 1777 * @throws IOException in case of an I/O error 1778 * @since 2.3 1779 */ 1780 public static List<String> readLines(File file, Charset encoding) throws IOException { 1781 InputStream in = null; 1782 try { 1783 in = openInputStream(file); 1784 return IOUtils.readLines(in, Charsets.toCharset(encoding)); 1785 } finally { 1786 IOUtils.closeQuietly(in); 1787 } 1788 } 1789 1790 /** 1791 * Reads the contents of a file line by line to a List of Strings. The file is always closed. 1792 * 1793 * @param file 1794 * the file to read, must not be {@code null} 1795 * @param encoding 1796 * the encoding to use, {@code null} means platform default 1797 * @return the list of Strings representing each line in the file, never {@code null} 1798 * @throws IOException 1799 * in case of an I/O error 1800 * @throws UnsupportedCharsetException 1801 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 1802 * supported. 1803 * @since 1.1 1804 */ 1805 public static List<String> readLines(File file, String encoding) throws IOException { 1806 return readLines(file, Charsets.toCharset(encoding)); 1807 } 1808 1809 /** 1810 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. 1811 * The file is always closed. 1812 * 1813 * @param file the file to read, must not be {@code null} 1814 * @return the list of Strings representing each line in the file, never {@code null} 1815 * @throws IOException in case of an I/O error 1816 * @since 1.3 1817 */ 1818 public static List<String> readLines(File file) throws IOException { 1819 return readLines(file, Charset.defaultCharset()); 1820 } 1821 1822 /** 1823 * Returns an Iterator for the lines in a <code>File</code>. 1824 * <p> 1825 * This method opens an <code>InputStream</code> for the file. 1826 * When you have finished with the iterator you should close the stream 1827 * to free internal resources. This can be done by calling the 1828 * {@link LineIterator#close()} or 1829 * {@link LineIterator#closeQuietly(LineIterator)} method. 1830 * <p> 1831 * The recommended usage pattern is: 1832 * <pre> 1833 * LineIterator it = FileUtils.lineIterator(file, "UTF-8"); 1834 * try { 1835 * while (it.hasNext()) { 1836 * String line = it.nextLine(); 1837 * /// do something with line 1838 * } 1839 * } finally { 1840 * LineIterator.closeQuietly(iterator); 1841 * } 1842 * </pre> 1843 * <p> 1844 * If an exception occurs during the creation of the iterator, the 1845 * underlying stream is closed. 1846 * 1847 * @param file the file to open for input, must not be {@code null} 1848 * @param encoding the encoding to use, {@code null} means platform default 1849 * @return an Iterator of the lines in the file, never {@code null} 1850 * @throws IOException in case of an I/O error (file closed) 1851 * @since 1.2 1852 */ 1853 public static LineIterator lineIterator(File file, String encoding) throws IOException { 1854 InputStream in = null; 1855 try { 1856 in = openInputStream(file); 1857 return IOUtils.lineIterator(in, encoding); 1858 } catch (IOException ex) { 1859 IOUtils.closeQuietly(in); 1860 throw ex; 1861 } catch (RuntimeException ex) { 1862 IOUtils.closeQuietly(in); 1863 throw ex; 1864 } 1865 } 1866 1867 /** 1868 * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM. 1869 * 1870 * @param file the file to open for input, must not be {@code null} 1871 * @return an Iterator of the lines in the file, never {@code null} 1872 * @throws IOException in case of an I/O error (file closed) 1873 * @since 1.3 1874 * @see #lineIterator(File, String) 1875 */ 1876 public static LineIterator lineIterator(File file) throws IOException { 1877 return lineIterator(file, null); 1878 } 1879 1880 //----------------------------------------------------------------------- 1881 /** 1882 * Writes a String to a file creating the file if it does not exist. 1883 * 1884 * NOTE: As from v1.3, the parent directories of the file will be created 1885 * if they do not exist. 1886 * 1887 * @param file the file to write 1888 * @param data the content to write to the file 1889 * @param encoding the encoding to use, {@code null} means platform default 1890 * @throws IOException in case of an I/O error 1891 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1892 * @since 2.4 1893 */ 1894 public static void writeStringToFile(File file, String data, Charset encoding) throws IOException { 1895 writeStringToFile(file, data, encoding, false); 1896 } 1897 1898 /** 1899 * Writes a String to a file creating the file if it does not exist. 1900 * 1901 * NOTE: As from v1.3, the parent directories of the file will be created 1902 * if they do not exist. 1903 * 1904 * @param file the file to write 1905 * @param data the content to write to the file 1906 * @param encoding the encoding to use, {@code null} means platform default 1907 * @throws IOException in case of an I/O error 1908 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1909 */ 1910 public static void writeStringToFile(File file, String data, String encoding) throws IOException { 1911 writeStringToFile(file, data, encoding, false); 1912 } 1913 1914 /** 1915 * Writes a String to a file creating the file if it does not exist. 1916 * 1917 * @param file the file to write 1918 * @param data the content to write to the file 1919 * @param encoding the encoding to use, {@code null} means platform default 1920 * @param append if {@code true}, then the String will be added to the 1921 * end of the file rather than overwriting 1922 * @throws IOException in case of an I/O error 1923 * @since 2.3 1924 */ 1925 public static void writeStringToFile(File file, String data, Charset encoding, boolean append) throws IOException { 1926 OutputStream out = null; 1927 try { 1928 out = openOutputStream(file, append); 1929 IOUtils.write(data, out, encoding); 1930 out.close(); // don't swallow close Exception if copy completes normally 1931 } finally { 1932 IOUtils.closeQuietly(out); 1933 } 1934 } 1935 1936 /** 1937 * Writes a String to a file creating the file if it does not exist. 1938 * 1939 * @param file the file to write 1940 * @param data the content to write to the file 1941 * @param encoding the encoding to use, {@code null} means platform default 1942 * @param append if {@code true}, then the String will be added to the 1943 * end of the file rather than overwriting 1944 * @throws IOException in case of an I/O error 1945 * @throws UnsupportedCharsetException 1946 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 1947 * supported by the VM 1948 * @since 2.1 1949 */ 1950 public static void writeStringToFile(File file, String data, String encoding, boolean append) throws IOException { 1951 writeStringToFile(file, data, Charsets.toCharset(encoding), append); 1952 } 1953 1954 /** 1955 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1956 * 1957 * @param file the file to write 1958 * @param data the content to write to the file 1959 * @throws IOException in case of an I/O error 1960 */ 1961 public static void writeStringToFile(File file, String data) throws IOException { 1962 writeStringToFile(file, data, Charset.defaultCharset(), false); 1963 } 1964 1965 /** 1966 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1967 * 1968 * @param file the file to write 1969 * @param data the content to write to the file 1970 * @param append if {@code true}, then the String will be added to the 1971 * end of the file rather than overwriting 1972 * @throws IOException in case of an I/O error 1973 * @since 2.1 1974 */ 1975 public static void writeStringToFile(File file, String data, boolean append) throws IOException { 1976 writeStringToFile(file, data, Charset.defaultCharset(), append); 1977 } 1978 1979 /** 1980 * Writes a CharSequence 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 * @since 2.0 1986 */ 1987 public static void write(File file, CharSequence data) throws IOException { 1988 write(file, data, Charset.defaultCharset(), false); 1989 } 1990 1991 /** 1992 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. 1993 * 1994 * @param file the file to write 1995 * @param data the content to write to the file 1996 * @param append if {@code true}, then the data will be added to the 1997 * end of the file rather than overwriting 1998 * @throws IOException in case of an I/O error 1999 * @since 2.1 2000 */ 2001 public static void write(File file, CharSequence data, boolean append) throws IOException { 2002 write(file, data, Charset.defaultCharset(), append); 2003 } 2004 2005 /** 2006 * Writes a CharSequence to a file creating the file if it does not exist. 2007 * 2008 * @param file the file to write 2009 * @param data the content to write to the file 2010 * @param encoding the encoding to use, {@code null} means platform default 2011 * @throws IOException in case of an I/O error 2012 * @since 2.3 2013 */ 2014 public static void write(File file, CharSequence data, Charset encoding) throws IOException { 2015 write(file, data, encoding, false); 2016 } 2017 2018 /** 2019 * Writes a CharSequence to a file creating the file if it does not exist. 2020 * 2021 * @param file the file to write 2022 * @param data the content to write to the file 2023 * @param encoding the encoding to use, {@code null} means platform default 2024 * @throws IOException in case of an I/O error 2025 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2026 * @since 2.0 2027 */ 2028 public static void write(File file, CharSequence data, String encoding) throws IOException { 2029 write(file, data, encoding, false); 2030 } 2031 2032 /** 2033 * Writes a CharSequence to a file creating the file if it does not exist. 2034 * 2035 * @param file the file to write 2036 * @param data the content to write to the file 2037 * @param encoding the encoding to use, {@code null} means platform default 2038 * @param append if {@code true}, then the data will be added to the 2039 * end of the file rather than overwriting 2040 * @throws IOException in case of an I/O error 2041 * @since 2.3 2042 */ 2043 public static void write(File file, CharSequence data, Charset encoding, boolean append) throws IOException { 2044 String str = data == null ? null : data.toString(); 2045 writeStringToFile(file, str, encoding, append); 2046 } 2047 2048 /** 2049 * Writes a CharSequence to a file creating the file if it does not exist. 2050 * 2051 * @param file the file to write 2052 * @param data the content to write to the file 2053 * @param encoding the encoding to use, {@code null} means platform default 2054 * @param append if {@code true}, then the data will be added to the 2055 * end of the file rather than overwriting 2056 * @throws IOException in case of an I/O error 2057 * @throws UnsupportedCharsetException 2058 * thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not 2059 * supported by the VM 2060 * @since IO 2.1 2061 */ 2062 public static void write(File file, CharSequence data, String encoding, boolean append) throws IOException { 2063 write(file, data, Charsets.toCharset(encoding), append); 2064 } 2065 2066 /** 2067 * Writes a byte array to a file creating the file if it does not exist. 2068 * <p> 2069 * NOTE: As from v1.3, the parent directories of the file will be created 2070 * if they do not exist. 2071 * 2072 * @param file the file to write to 2073 * @param data the content to write to the file 2074 * @throws IOException in case of an I/O error 2075 * @since 1.1 2076 */ 2077 public static void writeByteArrayToFile(File file, byte[] data) throws IOException { 2078 writeByteArrayToFile(file, data, false); 2079 } 2080 2081 /** 2082 * Writes a byte array to a file creating the file if it does not exist. 2083 * 2084 * @param file the file to write to 2085 * @param data the content to write to the file 2086 * @param append if {@code true}, then bytes will be added to the 2087 * end of the file rather than overwriting 2088 * @throws IOException in case of an I/O error 2089 * @since IO 2.1 2090 */ 2091 public static void writeByteArrayToFile(File file, byte[] data, boolean append) throws IOException { 2092 OutputStream out = null; 2093 try { 2094 out = openOutputStream(file, append); 2095 out.write(data); 2096 out.close(); // don't swallow close Exception if copy completes normally 2097 } finally { 2098 IOUtils.closeQuietly(out); 2099 } 2100 } 2101 2102 /** 2103 * Writes the <code>toString()</code> value of each item in a collection to 2104 * the specified <code>File</code> line by line. 2105 * The specified character encoding and the default line ending will be used. 2106 * <p> 2107 * NOTE: As from v1.3, the parent directories of the file will be created 2108 * if they do not exist. 2109 * 2110 * @param file the file to write to 2111 * @param encoding the encoding to use, {@code null} means platform default 2112 * @param lines the lines to write, {@code null} entries produce blank lines 2113 * @throws IOException in case of an I/O error 2114 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2115 * @since 1.1 2116 */ 2117 public static void writeLines(File file, String encoding, Collection<?> lines) throws IOException { 2118 writeLines(file, encoding, lines, null, false); 2119 } 2120 2121 /** 2122 * Writes the <code>toString()</code> value of each item in a collection to 2123 * the specified <code>File</code> line by line, optionally appending. 2124 * The specified character encoding and the default line ending will be used. 2125 * 2126 * @param file the file to write to 2127 * @param encoding the encoding to use, {@code null} means platform default 2128 * @param lines the lines to write, {@code null} entries produce blank lines 2129 * @param append if {@code true}, then the lines will be added to the 2130 * end of the file rather than overwriting 2131 * @throws IOException in case of an I/O error 2132 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2133 * @since 2.1 2134 */ 2135 public static void writeLines(File file, String encoding, Collection<?> lines, boolean append) throws IOException { 2136 writeLines(file, encoding, lines, null, append); 2137 } 2138 2139 /** 2140 * Writes the <code>toString()</code> value of each item in a collection to 2141 * the specified <code>File</code> line by line. 2142 * The default VM encoding and the default line ending will be used. 2143 * 2144 * @param file the file to write to 2145 * @param lines the lines to write, {@code null} entries produce blank lines 2146 * @throws IOException in case of an I/O error 2147 * @since 1.3 2148 */ 2149 public static void writeLines(File file, Collection<?> lines) throws IOException { 2150 writeLines(file, null, lines, null, false); 2151 } 2152 2153 /** 2154 * Writes the <code>toString()</code> value of each item in a collection to 2155 * the specified <code>File</code> line by line. 2156 * The default VM encoding and the default line ending will be used. 2157 * 2158 * @param file the file to write to 2159 * @param lines the lines to write, {@code null} entries produce blank lines 2160 * @param append if {@code true}, then the lines will be added to the 2161 * end of the file rather than overwriting 2162 * @throws IOException in case of an I/O error 2163 * @since 2.1 2164 */ 2165 public static void writeLines(File file, Collection<?> lines, boolean append) throws IOException { 2166 writeLines(file, null, lines, null, append); 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 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 * @param lineEnding the line separator to use, {@code null} is system default 2181 * @throws IOException in case of an I/O error 2182 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2183 * @since 1.1 2184 */ 2185 public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding) 2186 throws IOException { 2187 writeLines(file, encoding, lines, lineEnding, false); 2188 } 2189 2190 /** 2191 * Writes the <code>toString()</code> value of each item in a collection to 2192 * the specified <code>File</code> line by line. 2193 * The specified character encoding and the line ending will be used. 2194 * 2195 * @param file the file to write to 2196 * @param encoding the encoding to use, {@code null} means platform default 2197 * @param lines the lines to write, {@code null} entries produce blank lines 2198 * @param lineEnding the line separator to use, {@code null} is system default 2199 * @param append if {@code true}, then the lines will be added to the 2200 * end of the file rather than overwriting 2201 * @throws IOException in case of an I/O error 2202 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 2203 * @since 2.1 2204 */ 2205 public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding, boolean append) 2206 throws IOException { 2207 FileOutputStream out = null; 2208 try { 2209 out = openOutputStream(file, append); 2210 final BufferedOutputStream buffer = new BufferedOutputStream(out); 2211 IOUtils.writeLines(lines, lineEnding, buffer, encoding); 2212 buffer.flush(); 2213 out.close(); // don't swallow close Exception if copy completes normally 2214 } finally { 2215 IOUtils.closeQuietly(out); 2216 } 2217 } 2218 2219 /** 2220 * Writes the <code>toString()</code> value of each item in a collection to 2221 * the specified <code>File</code> line by line. 2222 * The default VM encoding and the specified line ending will be used. 2223 * 2224 * @param file the file to write to 2225 * @param lines the lines to write, {@code null} entries produce blank lines 2226 * @param lineEnding the line separator to use, {@code null} is system default 2227 * @throws IOException in case of an I/O error 2228 * @since 1.3 2229 */ 2230 public static void writeLines(File file, Collection<?> lines, String lineEnding) throws IOException { 2231 writeLines(file, null, lines, lineEnding, false); 2232 } 2233 2234 /** 2235 * Writes the <code>toString()</code> value of each item in a collection to 2236 * the specified <code>File</code> line by line. 2237 * The default VM encoding and the specified line ending will be used. 2238 * 2239 * @param file the file to write to 2240 * @param lines the lines to write, {@code null} entries produce blank lines 2241 * @param lineEnding the line separator to use, {@code null} is system default 2242 * @param append if {@code true}, then the lines will be added to the 2243 * end of the file rather than overwriting 2244 * @throws IOException in case of an I/O error 2245 * @since 2.1 2246 */ 2247 public static void writeLines(File file, Collection<?> lines, String lineEnding, boolean append) 2248 throws IOException { 2249 writeLines(file, null, lines, lineEnding, append); 2250 } 2251 2252 //----------------------------------------------------------------------- 2253 /** 2254 * Deletes a file. If file is a directory, delete it and all sub-directories. 2255 * <p> 2256 * The difference between File.delete() and this method are: 2257 * <ul> 2258 * <li>A directory to be deleted does not have to be empty.</li> 2259 * <li>You get exceptions when a file or directory cannot be deleted. 2260 * (java.io.File methods returns a boolean)</li> 2261 * </ul> 2262 * 2263 * @param file file or directory to delete, must not be {@code null} 2264 * @throws NullPointerException if the directory is {@code null} 2265 * @throws FileNotFoundException if the file was not found 2266 * @throws IOException in case deletion is unsuccessful 2267 */ 2268 public static void forceDelete(File file) throws IOException { 2269 if (file.isDirectory()) { 2270 deleteDirectory(file); 2271 } else { 2272 boolean filePresent = file.exists(); 2273 if (!file.delete()) { 2274 if (!filePresent){ 2275 throw new FileNotFoundException("File does not exist: " + file); 2276 } 2277 String message = 2278 "Unable to delete file: " + file; 2279 throw new IOException(message); 2280 } 2281 } 2282 } 2283 2284 /** 2285 * Schedules a file to be deleted when JVM exits. 2286 * If file is directory delete it and all sub-directories. 2287 * 2288 * @param file file or directory to delete, must not be {@code null} 2289 * @throws NullPointerException if the file is {@code null} 2290 * @throws IOException in case deletion is unsuccessful 2291 */ 2292 public static void forceDeleteOnExit(File file) throws IOException { 2293 if (file.isDirectory()) { 2294 deleteDirectoryOnExit(file); 2295 } else { 2296 file.deleteOnExit(); 2297 } 2298 } 2299 2300 /** 2301 * Schedules a directory recursively for deletion on JVM exit. 2302 * 2303 * @param directory directory to delete, must not be {@code null} 2304 * @throws NullPointerException if the directory is {@code null} 2305 * @throws IOException in case deletion is unsuccessful 2306 */ 2307 private static void deleteDirectoryOnExit(File directory) throws IOException { 2308 if (!directory.exists()) { 2309 return; 2310 } 2311 2312 directory.deleteOnExit(); 2313 if (!isSymlink(directory)) { 2314 cleanDirectoryOnExit(directory); 2315 } 2316 } 2317 2318 /** 2319 * Cleans a directory without deleting it. 2320 * 2321 * @param directory directory to clean, must not be {@code null} 2322 * @throws NullPointerException if the directory is {@code null} 2323 * @throws IOException in case cleaning is unsuccessful 2324 */ 2325 private static void cleanDirectoryOnExit(File directory) throws IOException { 2326 if (!directory.exists()) { 2327 String message = directory + " does not exist"; 2328 throw new IllegalArgumentException(message); 2329 } 2330 2331 if (!directory.isDirectory()) { 2332 String message = directory + " is not a directory"; 2333 throw new IllegalArgumentException(message); 2334 } 2335 2336 File[] files = directory.listFiles(); 2337 if (files == null) { // null if security restricted 2338 throw new IOException("Failed to list contents of " + directory); 2339 } 2340 2341 IOException exception = null; 2342 for (File file : files) { 2343 try { 2344 forceDeleteOnExit(file); 2345 } catch (IOException ioe) { 2346 exception = ioe; 2347 } 2348 } 2349 2350 if (null != exception) { 2351 throw exception; 2352 } 2353 } 2354 2355 /** 2356 * Makes a directory, including any necessary but nonexistent parent 2357 * directories. If a file already exists with specified name but it is 2358 * not a directory then an IOException is thrown. 2359 * If the directory cannot be created (or does not already exist) 2360 * then an IOException is thrown. 2361 * 2362 * @param directory directory to create, must not be {@code null} 2363 * @throws NullPointerException if the directory is {@code null} 2364 * @throws IOException if the directory cannot be created or the file already exists but is not a directory 2365 */ 2366 public static void forceMkdir(File directory) throws IOException { 2367 if (directory.exists()) { 2368 if (!directory.isDirectory()) { 2369 String message = 2370 "File " 2371 + directory 2372 + " exists and is " 2373 + "not a directory. Unable to create directory."; 2374 throw new IOException(message); 2375 } 2376 } else { 2377 if (!directory.mkdirs()) { 2378 // Double-check that some other thread or process hasn't made 2379 // the directory in the background 2380 if (!directory.isDirectory()) 2381 { 2382 String message = 2383 "Unable to create directory " + directory; 2384 throw new IOException(message); 2385 } 2386 } 2387 } 2388 } 2389 2390 //----------------------------------------------------------------------- 2391 /** 2392 * Returns the size of the specified file or directory. If the provided 2393 * {@link File} is a regular file, then the file's length is returned. 2394 * If the argument is a directory, then the size of the directory is 2395 * calculated recursively. If a directory or subdirectory is security 2396 * restricted, its size will not be included. 2397 * 2398 * @param file the regular file or directory to return the size 2399 * of (must not be {@code null}). 2400 * 2401 * @return the length of the file, or recursive size of the directory, 2402 * provided (in bytes). 2403 * 2404 * @throws NullPointerException if the file is {@code null} 2405 * @throws IllegalArgumentException if the file does not exist. 2406 * 2407 * @since 2.0 2408 */ 2409 public static long sizeOf(File file) { 2410 2411 if (!file.exists()) { 2412 String message = file + " does not exist"; 2413 throw new IllegalArgumentException(message); 2414 } 2415 2416 if (file.isDirectory()) { 2417 return sizeOfDirectory(file); 2418 } else { 2419 return file.length(); 2420 } 2421 2422 } 2423 2424 /** 2425 * Returns the size of the specified file or directory. If the provided 2426 * {@link File} is a regular file, then the file's length is returned. 2427 * If the argument is a directory, then the size of the directory is 2428 * calculated recursively. If a directory or subdirectory is security 2429 * restricted, its size will not be included. 2430 * 2431 * @param file the regular file or directory to return the size 2432 * of (must not be {@code null}). 2433 * 2434 * @return the length of the file, or recursive size of the directory, 2435 * provided (in bytes). 2436 * 2437 * @throws NullPointerException if the file is {@code null} 2438 * @throws IllegalArgumentException if the file does not exist. 2439 * 2440 * @since 2.4 2441 */ 2442 public static BigInteger sizeOfAsBigInteger(File file) { 2443 2444 if (!file.exists()) { 2445 String message = file + " does not exist"; 2446 throw new IllegalArgumentException(message); 2447 } 2448 2449 if (file.isDirectory()) { 2450 return sizeOfDirectoryAsBigInteger(file); 2451 } else { 2452 return BigInteger.valueOf(file.length()); 2453 } 2454 2455 } 2456 2457 /** 2458 * Counts the size of a directory recursively (sum of the length of all files). 2459 * 2460 * @param directory 2461 * directory to inspect, must not be {@code null} 2462 * @return size of directory in bytes, 0 if directory is security restricted, a negative number when the real total 2463 * is greater than {@link Long#MAX_VALUE}. 2464 * @throws NullPointerException 2465 * if the directory is {@code null} 2466 */ 2467 public static long sizeOfDirectory(File directory) { 2468 checkDirectory(directory); 2469 2470 final File[] files = directory.listFiles(); 2471 if (files == null) { // null if security restricted 2472 return 0L; 2473 } 2474 long size = 0; 2475 2476 for (final File file : files) { 2477 try { 2478 if (!isSymlink(file)) { 2479 size += sizeOf(file); 2480 if (size < 0) { 2481 break; 2482 } 2483 } 2484 } catch (IOException ioe) { 2485 // Ignore exceptions caught when asking if a File is a symlink. 2486 } 2487 } 2488 2489 return size; 2490 } 2491 2492 /** 2493 * Counts the size of a directory recursively (sum of the length of all files). 2494 * 2495 * @param directory 2496 * directory to inspect, must not be {@code null} 2497 * @return size of directory in bytes, 0 if directory is security restricted. 2498 * @throws NullPointerException 2499 * if the directory is {@code null} 2500 * @since 2.4 2501 */ 2502 public static BigInteger sizeOfDirectoryAsBigInteger(File directory) { 2503 checkDirectory(directory); 2504 2505 final File[] files = directory.listFiles(); 2506 if (files == null) { // null if security restricted 2507 return BigInteger.ZERO; 2508 } 2509 BigInteger size = BigInteger.ZERO; 2510 2511 for (final File file : files) { 2512 try { 2513 if (!isSymlink(file)) { 2514 size = size.add(BigInteger.valueOf(sizeOf(file))); 2515 } 2516 } catch (IOException ioe) { 2517 // Ignore exceptions caught when asking if a File is a symlink. 2518 } 2519 } 2520 2521 return size; 2522 } 2523 2524 /** 2525 * Checks that the given {@code File} exists and is a directory. 2526 * 2527 * @param directory The {@code File} to check. 2528 * @throws IllegalArgumentException if the given {@code File} does not exist or is not a directory. 2529 */ 2530 private static void checkDirectory(File directory) { 2531 if (!directory.exists()) { 2532 throw new IllegalArgumentException(directory + " does not exist"); 2533 } 2534 if (!directory.isDirectory()) { 2535 throw new IllegalArgumentException(directory + " is not a directory"); 2536 } 2537 } 2538 2539 //----------------------------------------------------------------------- 2540 /** 2541 * Tests if the specified <code>File</code> is newer than the reference 2542 * <code>File</code>. 2543 * 2544 * @param file the <code>File</code> of which the modification date must 2545 * be compared, must not be {@code null} 2546 * @param reference the <code>File</code> of which the modification date 2547 * is used, must not be {@code null} 2548 * @return true if the <code>File</code> exists and has been modified more 2549 * recently than the reference <code>File</code> 2550 * @throws IllegalArgumentException if the file is {@code null} 2551 * @throws IllegalArgumentException if the reference file is {@code null} or doesn't exist 2552 */ 2553 public static boolean isFileNewer(File file, File reference) { 2554 if (reference == null) { 2555 throw new IllegalArgumentException("No specified reference file"); 2556 } 2557 if (!reference.exists()) { 2558 throw new IllegalArgumentException("The reference file '" 2559 + reference + "' doesn't exist"); 2560 } 2561 return isFileNewer(file, reference.lastModified()); 2562 } 2563 2564 /** 2565 * Tests if the specified <code>File</code> is newer than the specified 2566 * <code>Date</code>. 2567 * 2568 * @param file the <code>File</code> of which the modification date 2569 * must be compared, must not be {@code null} 2570 * @param date the date reference, must not be {@code null} 2571 * @return true if the <code>File</code> exists and has been modified 2572 * after the given <code>Date</code>. 2573 * @throws IllegalArgumentException if the file is {@code null} 2574 * @throws IllegalArgumentException if the date is {@code null} 2575 */ 2576 public static boolean isFileNewer(File file, Date date) { 2577 if (date == null) { 2578 throw new IllegalArgumentException("No specified date"); 2579 } 2580 return isFileNewer(file, date.getTime()); 2581 } 2582 2583 /** 2584 * Tests if the specified <code>File</code> is newer than the specified 2585 * time reference. 2586 * 2587 * @param file the <code>File</code> of which the modification date must 2588 * be compared, must not be {@code null} 2589 * @param timeMillis the time reference measured in milliseconds since the 2590 * epoch (00:00:00 GMT, January 1, 1970) 2591 * @return true if the <code>File</code> exists and has been modified after 2592 * the given time reference. 2593 * @throws IllegalArgumentException if the file is {@code null} 2594 */ 2595 public static boolean isFileNewer(File file, long timeMillis) { 2596 if (file == null) { 2597 throw new IllegalArgumentException("No specified file"); 2598 } 2599 if (!file.exists()) { 2600 return false; 2601 } 2602 return file.lastModified() > timeMillis; 2603 } 2604 2605 2606 //----------------------------------------------------------------------- 2607 /** 2608 * Tests if the specified <code>File</code> is older than the reference 2609 * <code>File</code>. 2610 * 2611 * @param file the <code>File</code> of which the modification date must 2612 * be compared, must not be {@code null} 2613 * @param reference the <code>File</code> of which the modification date 2614 * is used, must not be {@code null} 2615 * @return true if the <code>File</code> exists and has been modified before 2616 * the reference <code>File</code> 2617 * @throws IllegalArgumentException if the file is {@code null} 2618 * @throws IllegalArgumentException if the reference file is {@code null} or doesn't exist 2619 */ 2620 public static boolean isFileOlder(File file, File reference) { 2621 if (reference == null) { 2622 throw new IllegalArgumentException("No specified reference file"); 2623 } 2624 if (!reference.exists()) { 2625 throw new IllegalArgumentException("The reference file '" 2626 + reference + "' doesn't exist"); 2627 } 2628 return isFileOlder(file, reference.lastModified()); 2629 } 2630 2631 /** 2632 * Tests if the specified <code>File</code> is older than the specified 2633 * <code>Date</code>. 2634 * 2635 * @param file the <code>File</code> of which the modification date 2636 * must be compared, must not be {@code null} 2637 * @param date the date reference, must not be {@code null} 2638 * @return true if the <code>File</code> exists and has been modified 2639 * before the given <code>Date</code>. 2640 * @throws IllegalArgumentException if the file is {@code null} 2641 * @throws IllegalArgumentException if the date is {@code null} 2642 */ 2643 public static boolean isFileOlder(File file, Date date) { 2644 if (date == null) { 2645 throw new IllegalArgumentException("No specified date"); 2646 } 2647 return isFileOlder(file, date.getTime()); 2648 } 2649 2650 /** 2651 * Tests if the specified <code>File</code> is older than the specified 2652 * time reference. 2653 * 2654 * @param file the <code>File</code> of which the modification date must 2655 * be compared, must not be {@code null} 2656 * @param timeMillis the time reference measured in milliseconds since the 2657 * epoch (00:00:00 GMT, January 1, 1970) 2658 * @return true if the <code>File</code> exists and has been modified before 2659 * the given time reference. 2660 * @throws IllegalArgumentException if the file is {@code null} 2661 */ 2662 public static boolean isFileOlder(File file, long timeMillis) { 2663 if (file == null) { 2664 throw new IllegalArgumentException("No specified file"); 2665 } 2666 if (!file.exists()) { 2667 return false; 2668 } 2669 return file.lastModified() < timeMillis; 2670 } 2671 2672 //----------------------------------------------------------------------- 2673 /** 2674 * Computes the checksum of a file using the CRC32 checksum routine. 2675 * The value of the checksum is returned. 2676 * 2677 * @param file the file to checksum, must not be {@code null} 2678 * @return the checksum value 2679 * @throws NullPointerException if the file or checksum is {@code null} 2680 * @throws IllegalArgumentException if the file is a directory 2681 * @throws IOException if an IO error occurs reading the file 2682 * @since 1.3 2683 */ 2684 public static long checksumCRC32(File file) throws IOException { 2685 CRC32 crc = new CRC32(); 2686 checksum(file, crc); 2687 return crc.getValue(); 2688 } 2689 2690 /** 2691 * Computes the checksum of a file using the specified checksum object. 2692 * Multiple files may be checked using one <code>Checksum</code> instance 2693 * if desired simply by reusing the same checksum object. 2694 * For example: 2695 * <pre> 2696 * long csum = FileUtils.checksum(file, new CRC32()).getValue(); 2697 * </pre> 2698 * 2699 * @param file the file to checksum, must not be {@code null} 2700 * @param checksum the checksum object to be used, must not be {@code null} 2701 * @return the checksum specified, updated with the content of the file 2702 * @throws NullPointerException if the file or checksum is {@code null} 2703 * @throws IllegalArgumentException if the file is a directory 2704 * @throws IOException if an IO error occurs reading the file 2705 * @since 1.3 2706 */ 2707 public static Checksum checksum(File file, Checksum checksum) throws IOException { 2708 if (file.isDirectory()) { 2709 throw new IllegalArgumentException("Checksums can't be computed on directories"); 2710 } 2711 InputStream in = null; 2712 try { 2713 in = new CheckedInputStream(new FileInputStream(file), checksum); 2714 IOUtils.copy(in, new NullOutputStream()); 2715 } finally { 2716 IOUtils.closeQuietly(in); 2717 } 2718 return checksum; 2719 } 2720 2721 /** 2722 * Moves a directory. 2723 * <p> 2724 * When the destination directory is on another file system, do a "copy and delete". 2725 * 2726 * @param srcDir the directory to be moved 2727 * @param destDir the destination directory 2728 * @throws NullPointerException if source or destination is {@code null} 2729 * @throws FileExistsException if the destination directory exists 2730 * @throws IOException if source or destination is invalid 2731 * @throws IOException if an IO error occurs moving the file 2732 * @since 1.4 2733 */ 2734 public static void moveDirectory(File srcDir, File destDir) throws IOException { 2735 if (srcDir == null) { 2736 throw new NullPointerException("Source must not be null"); 2737 } 2738 if (destDir == null) { 2739 throw new NullPointerException("Destination must not be null"); 2740 } 2741 if (!srcDir.exists()) { 2742 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 2743 } 2744 if (!srcDir.isDirectory()) { 2745 throw new IOException("Source '" + srcDir + "' is not a directory"); 2746 } 2747 if (destDir.exists()) { 2748 throw new FileExistsException("Destination '" + destDir + "' already exists"); 2749 } 2750 boolean rename = srcDir.renameTo(destDir); 2751 if (!rename) { 2752 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) { 2753 throw new IOException("Cannot move directory: "+srcDir+" to a subdirectory of itself: "+destDir); 2754 } 2755 copyDirectory( srcDir, destDir ); 2756 deleteDirectory( srcDir ); 2757 if (srcDir.exists()) { 2758 throw new IOException("Failed to delete original directory '" + srcDir + 2759 "' after copy to '" + destDir + "'"); 2760 } 2761 } 2762 } 2763 2764 /** 2765 * Moves a directory to another directory. 2766 * 2767 * @param src the file to be moved 2768 * @param destDir the destination file 2769 * @param createDestDir If {@code true} create the destination directory, 2770 * otherwise if {@code false} throw an IOException 2771 * @throws NullPointerException if source or destination is {@code null} 2772 * @throws FileExistsException if the directory exists in the destination directory 2773 * @throws IOException if source or destination is invalid 2774 * @throws IOException if an IO error occurs moving the file 2775 * @since 1.4 2776 */ 2777 public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException { 2778 if (src == null) { 2779 throw new NullPointerException("Source must not be null"); 2780 } 2781 if (destDir == null) { 2782 throw new NullPointerException("Destination directory must not be null"); 2783 } 2784 if (!destDir.exists() && createDestDir) { 2785 destDir.mkdirs(); 2786 } 2787 if (!destDir.exists()) { 2788 throw new FileNotFoundException("Destination directory '" + destDir + 2789 "' does not exist [createDestDir=" + createDestDir +"]"); 2790 } 2791 if (!destDir.isDirectory()) { 2792 throw new IOException("Destination '" + destDir + "' is not a directory"); 2793 } 2794 moveDirectory(src, new File(destDir, src.getName())); 2795 2796 } 2797 2798 /** 2799 * Moves a file. 2800 * <p> 2801 * When the destination file is on another file system, do a "copy and delete". 2802 * 2803 * @param srcFile the file to be moved 2804 * @param destFile the destination file 2805 * @throws NullPointerException if source or destination is {@code null} 2806 * @throws FileExistsException if the destination file exists 2807 * @throws IOException if source or destination is invalid 2808 * @throws IOException if an IO error occurs moving the file 2809 * @since 1.4 2810 */ 2811 public static void moveFile(File srcFile, File destFile) throws IOException { 2812 if (srcFile == null) { 2813 throw new NullPointerException("Source must not be null"); 2814 } 2815 if (destFile == null) { 2816 throw new NullPointerException("Destination must not be null"); 2817 } 2818 if (!srcFile.exists()) { 2819 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 2820 } 2821 if (srcFile.isDirectory()) { 2822 throw new IOException("Source '" + srcFile + "' is a directory"); 2823 } 2824 if (destFile.exists()) { 2825 throw new FileExistsException("Destination '" + destFile + "' already exists"); 2826 } 2827 if (destFile.isDirectory()) { 2828 throw new IOException("Destination '" + destFile + "' is a directory"); 2829 } 2830 boolean rename = srcFile.renameTo(destFile); 2831 if (!rename) { 2832 copyFile( srcFile, destFile ); 2833 if (!srcFile.delete()) { 2834 FileUtils.deleteQuietly(destFile); 2835 throw new IOException("Failed to delete original file '" + srcFile + 2836 "' after copy to '" + destFile + "'"); 2837 } 2838 } 2839 } 2840 2841 /** 2842 * Moves a file to a directory. 2843 * 2844 * @param srcFile the file to be moved 2845 * @param destDir the destination file 2846 * @param createDestDir If {@code true} create the destination directory, 2847 * otherwise if {@code false} throw an IOException 2848 * @throws NullPointerException if source or destination is {@code null} 2849 * @throws FileExistsException if the destination file exists 2850 * @throws IOException if source or destination is invalid 2851 * @throws IOException if an IO error occurs moving the file 2852 * @since 1.4 2853 */ 2854 public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException { 2855 if (srcFile == null) { 2856 throw new NullPointerException("Source must not be null"); 2857 } 2858 if (destDir == null) { 2859 throw new NullPointerException("Destination directory must not be null"); 2860 } 2861 if (!destDir.exists() && createDestDir) { 2862 destDir.mkdirs(); 2863 } 2864 if (!destDir.exists()) { 2865 throw new FileNotFoundException("Destination directory '" + destDir + 2866 "' does not exist [createDestDir=" + createDestDir +"]"); 2867 } 2868 if (!destDir.isDirectory()) { 2869 throw new IOException("Destination '" + destDir + "' is not a directory"); 2870 } 2871 moveFile(srcFile, new File(destDir, srcFile.getName())); 2872 } 2873 2874 /** 2875 * Moves a file or directory to the destination directory. 2876 * <p> 2877 * When the destination is on another file system, do a "copy and delete". 2878 * 2879 * @param src the file or directory to be moved 2880 * @param destDir the destination directory 2881 * @param createDestDir If {@code true} create the destination directory, 2882 * otherwise if {@code false} throw an IOException 2883 * @throws NullPointerException if source or destination is {@code null} 2884 * @throws FileExistsException if the directory or file exists in the destination directory 2885 * @throws IOException if source or destination is invalid 2886 * @throws IOException if an IO error occurs moving the file 2887 * @since 1.4 2888 */ 2889 public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException { 2890 if (src == null) { 2891 throw new NullPointerException("Source must not be null"); 2892 } 2893 if (destDir == null) { 2894 throw new NullPointerException("Destination must not be null"); 2895 } 2896 if (!src.exists()) { 2897 throw new FileNotFoundException("Source '" + src + "' does not exist"); 2898 } 2899 if (src.isDirectory()) { 2900 moveDirectoryToDirectory(src, destDir, createDestDir); 2901 } else { 2902 moveFileToDirectory(src, destDir, createDestDir); 2903 } 2904 } 2905 2906 /** 2907 * Determines whether the specified file is a Symbolic Link rather than an actual file. 2908 * <p> 2909 * Will not return true if there is a Symbolic Link anywhere in the path, 2910 * only if the specific file is. 2911 * <p> 2912 * <b>Note:</b> the current implementation always returns {@code false} if the system 2913 * is detected as Windows using {@link FilenameUtils#isSystemWindows()} 2914 * 2915 * @param file the file to check 2916 * @return true if the file is a Symbolic Link 2917 * @throws IOException if an IO error occurs while checking the file 2918 * @since 2.0 2919 */ 2920 public static boolean isSymlink(File file) throws IOException { 2921 if (file == null) { 2922 throw new NullPointerException("File must not be null"); 2923 } 2924 if (FilenameUtils.isSystemWindows()) { 2925 return false; 2926 } 2927 File fileInCanonicalDir = null; 2928 if (file.getParent() == null) { 2929 fileInCanonicalDir = file; 2930 } else { 2931 File canonicalDir = file.getParentFile().getCanonicalFile(); 2932 fileInCanonicalDir = new File(canonicalDir, file.getName()); 2933 } 2934 2935 if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) { 2936 return false; 2937 } else { 2938 return true; 2939 } 2940 } 2941 2942 }