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