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