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