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