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