1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.io; 18 19 import com.google.common.annotations.VisibleForTesting; 20 21 import java.io.File; 22 import java.io.FileFilter; 23 import java.io.FileInputStream; 24 import java.io.FileNotFoundException; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.OutputStream; 29 import java.net.URL; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.Date; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.zip.CRC32; 36 import java.util.zip.CheckedInputStream; 37 import java.util.zip.Checksum; 38 39 import org.apache.commons.io.filefilter.DirectoryFileFilter; 40 import org.apache.commons.io.filefilter.FalseFileFilter; 41 import org.apache.commons.io.filefilter.FileFilterUtils; 42 import org.apache.commons.io.filefilter.IOFileFilter; 43 import org.apache.commons.io.filefilter.SuffixFileFilter; 44 import org.apache.commons.io.filefilter.TrueFileFilter; 45 import org.apache.commons.io.output.NullOutputStream; 46 47 /** 48 * General file manipulation utilities. 49 * <p> 50 * Facilities are provided in the following areas: 51 * <ul> 52 * <li>writing to a file 53 * <li>reading from a file 54 * <li>make a directory including parent directories 55 * <li>copying files and directories 56 * <li>deleting files and directories 57 * <li>converting to and from a URL 58 * <li>listing files and directories by filter and extension 59 * <li>comparing file content 60 * <li>file last changed date 61 * <li>calculating a checksum 62 * </ul> 63 * <p> 64 * Origin of code: Excalibur, Alexandria, Commons-Utils 65 * 66 * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A> 67 * @author <a href="mailto:sanders@apache.org">Scott Sanders</a> 68 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 69 * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a> 70 * @author <a href="mailto:peter@apache.org">Peter Donald</a> 71 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a> 72 * @author Matthew Hawthorne 73 * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a> 74 * @author Stephen Colebourne 75 * @author Ian Springer 76 * @author Chris Eldredge 77 * @author Jim Harrington 78 * @author Niall Pemberton 79 * @author Sandy McArthur 80 * @version $Id: FileUtils.java 610810 2008-01-10 15:04:49Z niallp $ 81 */ 82 @VisibleForTesting 83 public class FileUtils { 84 85 /** 86 * Instances should NOT be constructed in standard programming. 87 */ FileUtils()88 public FileUtils() { 89 super(); 90 } 91 92 /** 93 * The number of bytes in a kilobyte. 94 */ 95 public static final long ONE_KB = 1024; 96 97 /** 98 * The number of bytes in a megabyte. 99 */ 100 public static final long ONE_MB = ONE_KB * ONE_KB; 101 102 /** 103 * The number of bytes in a gigabyte. 104 */ 105 public static final long ONE_GB = ONE_KB * ONE_MB; 106 107 /** 108 * An empty array of type <code>File</code>. 109 */ 110 public static final File[] EMPTY_FILE_ARRAY = new File[0]; 111 112 //----------------------------------------------------------------------- 113 /** 114 * Opens a {@link FileInputStream} for the specified file, providing better 115 * error messages than simply calling <code>new FileInputStream(file)</code>. 116 * <p> 117 * At the end of the method either the stream will be successfully opened, 118 * or an exception will have been thrown. 119 * <p> 120 * An exception is thrown if the file does not exist. 121 * An exception is thrown if the file object exists but is a directory. 122 * An exception is thrown if the file exists but cannot be read. 123 * 124 * @param file the file to open for input, must not be <code>null</code> 125 * @return a new {@link FileInputStream} for the specified file 126 * @throws FileNotFoundException if the file does not exist 127 * @throws IOException if the file object is a directory 128 * @throws IOException if the file cannot be read 129 * @since Commons IO 1.3 130 */ openInputStream(File file)131 public static FileInputStream openInputStream(File file) throws IOException { 132 if (file.exists()) { 133 if (file.isDirectory()) { 134 throw new IOException("File '" + file + "' exists but is a directory"); 135 } 136 if (file.canRead() == false) { 137 throw new IOException("File '" + file + "' cannot be read"); 138 } 139 } else { 140 throw new FileNotFoundException("File '" + file + "' does not exist"); 141 } 142 return new FileInputStream(file); 143 } 144 145 //----------------------------------------------------------------------- 146 /** 147 * Opens a {@link FileOutputStream} for the specified file, checking and 148 * creating the parent directory if it does not exist. 149 * <p> 150 * At the end of the method either the stream will be successfully opened, 151 * or an exception will have been thrown. 152 * <p> 153 * The parent directory will be created if it does not exist. 154 * The file will be created if it does not exist. 155 * An exception is thrown if the file object exists but is a directory. 156 * An exception is thrown if the file exists but cannot be written to. 157 * An exception is thrown if the parent directory cannot be created. 158 * 159 * @param file the file to open for output, must not be <code>null</code> 160 * @return a new {@link FileOutputStream} for the specified file 161 * @throws IOException if the file object is a directory 162 * @throws IOException if the file cannot be written to 163 * @throws IOException if a parent directory needs creating but that fails 164 * @since Commons IO 1.3 165 */ openOutputStream(File file)166 public static FileOutputStream openOutputStream(File file) throws IOException { 167 if (file.exists()) { 168 if (file.isDirectory()) { 169 throw new IOException("File '" + file + "' exists but is a directory"); 170 } 171 if (file.canWrite() == false) { 172 throw new IOException("File '" + file + "' cannot be written to"); 173 } 174 } else { 175 File parent = file.getParentFile(); 176 if (parent != null && parent.exists() == false) { 177 if (parent.mkdirs() == false) { 178 throw new IOException("File '" + file + "' could not be created"); 179 } 180 } 181 } 182 return new FileOutputStream(file); 183 } 184 185 //----------------------------------------------------------------------- 186 /** 187 * Returns a human-readable version of the file size, where the input 188 * represents a specific number of bytes. 189 * 190 * @param size the number of bytes 191 * @return a human-readable display value (includes units) 192 */ byteCountToDisplaySize(long size)193 public static String byteCountToDisplaySize(long size) { 194 String displaySize; 195 196 if (size / ONE_GB > 0) { 197 displaySize = String.valueOf(size / ONE_GB) + " GB"; 198 } else if (size / ONE_MB > 0) { 199 displaySize = String.valueOf(size / ONE_MB) + " MB"; 200 } else if (size / ONE_KB > 0) { 201 displaySize = String.valueOf(size / ONE_KB) + " KB"; 202 } else { 203 displaySize = String.valueOf(size) + " bytes"; 204 } 205 return displaySize; 206 } 207 208 //----------------------------------------------------------------------- 209 /** 210 * Implements the same behaviour as the "touch" utility on Unix. It creates 211 * a new file with size 0 or, if the file exists already, it is opened and 212 * closed without modifying it, but updating the file date and time. 213 * <p> 214 * NOTE: As from v1.3, this method throws an IOException if the last 215 * modified date of the file cannot be set. Also, as from v1.3 this method 216 * creates parent directories if they do not exist. 217 * 218 * @param file the File to touch 219 * @throws IOException If an I/O problem occurs 220 */ touch(File file)221 public static void touch(File file) throws IOException { 222 if (!file.exists()) { 223 OutputStream out = openOutputStream(file); 224 IOUtils.closeQuietly(out); 225 } 226 boolean success = file.setLastModified(System.currentTimeMillis()); 227 if (!success) { 228 throw new IOException("Unable to set the last modification time for " + file); 229 } 230 } 231 232 //----------------------------------------------------------------------- 233 /** 234 * Converts a Collection containing java.io.File instanced into array 235 * representation. This is to account for the difference between 236 * File.listFiles() and FileUtils.listFiles(). 237 * 238 * @param files a Collection containing java.io.File instances 239 * @return an array of java.io.File 240 */ convertFileCollectionToFileArray(Collection<File> files)241 public static File[] convertFileCollectionToFileArray(Collection<File> files) { 242 return files.toArray(new File[files.size()]); 243 } 244 245 //----------------------------------------------------------------------- 246 /** 247 * Finds files within a given directory (and optionally its 248 * subdirectories). All files found are filtered by an IOFileFilter. 249 * 250 * @param files the collection of files found. 251 * @param directory the directory to search in. 252 * @param filter the filter to apply to files and directories. 253 */ innerListFiles(Collection<File> files, File directory, IOFileFilter filter)254 private static void innerListFiles(Collection<File> files, File directory, 255 IOFileFilter filter) { 256 File[] found = directory.listFiles((FileFilter) filter); 257 if (found != null) { 258 for (int i = 0; i < found.length; i++) { 259 if (found[i].isDirectory()) { 260 innerListFiles(files, found[i], filter); 261 } else { 262 files.add(found[i]); 263 } 264 } 265 } 266 } 267 268 /** 269 * Finds files within a given directory (and optionally its 270 * subdirectories). All files found are filtered by an IOFileFilter. 271 * <p> 272 * If your search should recurse into subdirectories you can pass in 273 * an IOFileFilter for directories. You don't need to bind a 274 * DirectoryFileFilter (via logical AND) to this filter. This method does 275 * that for you. 276 * <p> 277 * An example: If you want to search through all directories called 278 * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code> 279 * <p> 280 * Another common usage of this method is find files in a directory 281 * tree but ignoring the directories generated CVS. You can simply pass 282 * in <code>FileFilterUtils.makeCVSAware(null)</code>. 283 * 284 * @param directory the directory to search in 285 * @param fileFilter filter to apply when finding files. 286 * @param dirFilter optional filter to apply when finding subdirectories. 287 * If this parameter is <code>null</code>, subdirectories will not be included in the 288 * search. Use TrueFileFilter.INSTANCE to match all directories. 289 * @return an collection of java.io.File with the matching files 290 * @see org.apache.commons.io.filefilter.FileFilterUtils 291 * @see org.apache.commons.io.filefilter.NameFileFilter 292 */ listFiles( File directory, IOFileFilter fileFilter, IOFileFilter dirFilter)293 public static Collection<File> listFiles( 294 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 295 if (!directory.isDirectory()) { 296 throw new IllegalArgumentException( 297 "Parameter 'directory' is not a directory"); 298 } 299 if (fileFilter == null) { 300 throw new NullPointerException("Parameter 'fileFilter' is null"); 301 } 302 303 //Setup effective file filter 304 IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter, 305 FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE)); 306 307 //Setup effective directory filter 308 IOFileFilter effDirFilter; 309 if (dirFilter == null) { 310 effDirFilter = FalseFileFilter.INSTANCE; 311 } else { 312 effDirFilter = FileFilterUtils.andFileFilter(dirFilter, 313 DirectoryFileFilter.INSTANCE); 314 } 315 316 //Find files 317 Collection<File> files = new java.util.LinkedList<File>(); 318 innerListFiles(files, directory, 319 FileFilterUtils.orFileFilter(effFileFilter, effDirFilter)); 320 return files; 321 } 322 323 /** 324 * Allows iteration over the files in given directory (and optionally 325 * its subdirectories). 326 * <p> 327 * All files found are filtered by an IOFileFilter. This method is 328 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}. 329 * 330 * @param directory the directory to search in 331 * @param fileFilter filter to apply when finding files. 332 * @param dirFilter optional filter to apply when finding subdirectories. 333 * If this parameter is <code>null</code>, subdirectories will not be included in the 334 * search. Use TrueFileFilter.INSTANCE to match all directories. 335 * @return an iterator of java.io.File for the matching files 336 * @see org.apache.commons.io.filefilter.FileFilterUtils 337 * @see org.apache.commons.io.filefilter.NameFileFilter 338 * @since Commons IO 1.2 339 */ iterateFiles( File directory, IOFileFilter fileFilter, IOFileFilter dirFilter)340 public static Iterator<File> iterateFiles( 341 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 342 return listFiles(directory, fileFilter, dirFilter).iterator(); 343 } 344 345 //----------------------------------------------------------------------- 346 /** 347 * Converts an array of file extensions to suffixes for use 348 * with IOFileFilters. 349 * 350 * @param extensions an array of extensions. Format: {"java", "xml"} 351 * @return an array of suffixes. Format: {".java", ".xml"} 352 */ toSuffixes(String[] extensions)353 private static String[] toSuffixes(String[] extensions) { 354 String[] suffixes = new String[extensions.length]; 355 for (int i = 0; i < extensions.length; i++) { 356 suffixes[i] = "." + extensions[i]; 357 } 358 return suffixes; 359 } 360 361 362 /** 363 * Finds files within a given directory (and optionally its subdirectories) 364 * which match an array of extensions. 365 * 366 * @param directory the directory to search in 367 * @param extensions an array of extensions, ex. {"java","xml"}. If this 368 * parameter is <code>null</code>, all files are returned. 369 * @param recursive if true all subdirectories are searched as well 370 * @return an collection of java.io.File with the matching files 371 */ listFiles( File directory, String[] extensions, boolean recursive)372 public static Collection<File> listFiles( 373 File directory, String[] extensions, boolean recursive) { 374 IOFileFilter filter; 375 if (extensions == null) { 376 filter = TrueFileFilter.INSTANCE; 377 } else { 378 String[] suffixes = toSuffixes(extensions); 379 filter = new SuffixFileFilter(suffixes); 380 } 381 return listFiles(directory, filter, 382 (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE)); 383 } 384 385 /** 386 * Allows iteration over the files in a given directory (and optionally 387 * its subdirectories) which match an array of extensions. This method 388 * is based on {@link #listFiles(File, String[], boolean)}. 389 * 390 * @param directory the directory to search in 391 * @param extensions an array of extensions, ex. {"java","xml"}. If this 392 * parameter is <code>null</code>, all files are returned. 393 * @param recursive if true all subdirectories are searched as well 394 * @return an iterator of java.io.File with the matching files 395 * @since Commons IO 1.2 396 */ iterateFiles( File directory, String[] extensions, boolean recursive)397 public static Iterator<File> iterateFiles( 398 File directory, String[] extensions, boolean recursive) { 399 return listFiles(directory, extensions, recursive).iterator(); 400 } 401 402 //----------------------------------------------------------------------- 403 /** 404 * Compares the contents of two files to determine if they are equal or not. 405 * <p> 406 * This method checks to see if the two files are different lengths 407 * or if they point to the same file, before resorting to byte-by-byte 408 * comparison of the contents. 409 * <p> 410 * Code origin: Avalon 411 * 412 * @param file1 the first file 413 * @param file2 the second file 414 * @return true if the content of the files are equal or they both don't 415 * exist, false otherwise 416 * @throws IOException in case of an I/O error 417 */ contentEquals(File file1, File file2)418 public static boolean contentEquals(File file1, File file2) throws IOException { 419 boolean file1Exists = file1.exists(); 420 if (file1Exists != file2.exists()) { 421 return false; 422 } 423 424 if (!file1Exists) { 425 // two not existing files are equal 426 return true; 427 } 428 429 if (file1.isDirectory() || file2.isDirectory()) { 430 // don't want to compare directory contents 431 throw new IOException("Can't compare directories, only files"); 432 } 433 434 if (file1.length() != file2.length()) { 435 // lengths differ, cannot be equal 436 return false; 437 } 438 439 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 440 // same file 441 return true; 442 } 443 444 InputStream input1 = null; 445 InputStream input2 = null; 446 try { 447 input1 = new FileInputStream(file1); 448 input2 = new FileInputStream(file2); 449 return IOUtils.contentEquals(input1, input2); 450 451 } finally { 452 IOUtils.closeQuietly(input1); 453 IOUtils.closeQuietly(input2); 454 } 455 } 456 457 //----------------------------------------------------------------------- 458 /** 459 * Convert from a <code>URL</code> to a <code>File</code>. 460 * <p> 461 * From version 1.1 this method will decode the URL. 462 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 463 * correctly decoded to <code>/my docs/file.txt</code>. 464 * 465 * @param url the file URL to convert, <code>null</code> returns <code>null</code> 466 * @return the equivalent <code>File</code> object, or <code>null</code> 467 * if the URL's protocol is not <code>file</code> 468 * @throws IllegalArgumentException if the file is incorrectly encoded 469 */ toFile(URL url)470 public static File toFile(URL url) { 471 if (url == null || !url.getProtocol().equals("file")) { 472 return null; 473 } else { 474 String filename = url.getFile().replace('/', File.separatorChar); 475 int pos =0; 476 while ((pos = filename.indexOf('%', pos)) >= 0) { 477 if (pos + 2 < filename.length()) { 478 String hexStr = filename.substring(pos + 1, pos + 3); 479 char ch = (char) Integer.parseInt(hexStr, 16); 480 filename = filename.substring(0, pos) + ch + filename.substring(pos + 3); 481 } 482 } 483 return new File(filename); 484 } 485 } 486 487 /** 488 * Converts each of an array of <code>URL</code> to a <code>File</code>. 489 * <p> 490 * Returns an array of the same size as the input. 491 * If the input is <code>null</code>, an empty array is returned. 492 * If the input contains <code>null</code>, the output array contains <code>null</code> at the same 493 * index. 494 * <p> 495 * This method will decode the URL. 496 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 497 * correctly decoded to <code>/my docs/file.txt</code>. 498 * 499 * @param urls the file URLs to convert, <code>null</code> returns empty array 500 * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item 501 * if there was a <code>null</code> at that index in the input array 502 * @throws IllegalArgumentException if any file is not a URL file 503 * @throws IllegalArgumentException if any file is incorrectly encoded 504 * @since Commons IO 1.1 505 */ toFiles(URL[] urls)506 public static File[] toFiles(URL[] urls) { 507 if (urls == null || urls.length == 0) { 508 return EMPTY_FILE_ARRAY; 509 } 510 File[] files = new File[urls.length]; 511 for (int i = 0; i < urls.length; i++) { 512 URL url = urls[i]; 513 if (url != null) { 514 if (url.getProtocol().equals("file") == false) { 515 throw new IllegalArgumentException( 516 "URL could not be converted to a File: " + url); 517 } 518 files[i] = toFile(url); 519 } 520 } 521 return files; 522 } 523 524 /** 525 * Converts each of an array of <code>File</code> to a <code>URL</code>. 526 * <p> 527 * Returns an array of the same size as the input. 528 * 529 * @param files the files to convert 530 * @return an array of URLs matching the input 531 * @throws IOException if a file cannot be converted 532 */ toURLs(File[] files)533 public static URL[] toURLs(File[] files) throws IOException { 534 URL[] urls = new URL[files.length]; 535 536 for (int i = 0; i < urls.length; i++) { 537 urls[i] = files[i].toURI().toURL(); 538 } 539 540 return urls; 541 } 542 543 //----------------------------------------------------------------------- 544 /** 545 * Copies a file to a directory preserving the file date. 546 * <p> 547 * This method copies the contents of the specified source file 548 * to a file of the same name in the specified destination directory. 549 * The destination directory is created if it does not exist. 550 * If the destination file exists, then this method will overwrite it. 551 * 552 * @param srcFile an existing file to copy, must not be <code>null</code> 553 * @param destDir the directory to place the copy in, must not be <code>null</code> 554 * 555 * @throws NullPointerException if source or destination is null 556 * @throws IOException if source or destination is invalid 557 * @throws IOException if an IO error occurs during copying 558 * @see #copyFile(File, File, boolean) 559 */ copyFileToDirectory(File srcFile, File destDir)560 public static void copyFileToDirectory(File srcFile, File destDir) throws IOException { 561 copyFileToDirectory(srcFile, destDir, true); 562 } 563 564 /** 565 * Copies a file to a directory optionally preserving the file date. 566 * <p> 567 * This method copies the contents of the specified source file 568 * to a file of the same name in the specified destination directory. 569 * The destination directory is created if it does not exist. 570 * If the destination file exists, then this method will overwrite it. 571 * 572 * @param srcFile an existing file to copy, must not be <code>null</code> 573 * @param destDir the directory to place the copy in, must not be <code>null</code> 574 * @param preserveFileDate true if the file date of the copy 575 * should be the same as the original 576 * 577 * @throws NullPointerException if source or destination is <code>null</code> 578 * @throws IOException if source or destination is invalid 579 * @throws IOException if an IO error occurs during copying 580 * @see #copyFile(File, File, boolean) 581 * @since Commons IO 1.3 582 */ copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate)583 public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException { 584 if (destDir == null) { 585 throw new NullPointerException("Destination must not be null"); 586 } 587 if (destDir.exists() && destDir.isDirectory() == false) { 588 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 589 } 590 copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate); 591 } 592 593 /** 594 * Copies a file to a new location preserving the file date. 595 * <p> 596 * This method copies the contents of the specified source file to the 597 * specified destination file. The directory holding the destination file is 598 * created if it does not exist. If the destination file exists, then this 599 * method will overwrite it. 600 * 601 * @param srcFile an existing file to copy, must not be <code>null</code> 602 * @param destFile the new file, must not be <code>null</code> 603 * 604 * @throws NullPointerException if source or destination is <code>null</code> 605 * @throws IOException if source or destination is invalid 606 * @throws IOException if an IO error occurs during copying 607 * @see #copyFileToDirectory(File, File) 608 */ copyFile(File srcFile, File destFile)609 public static void copyFile(File srcFile, File destFile) throws IOException { 610 copyFile(srcFile, destFile, true); 611 } 612 613 /** 614 * Copies a file to a new location. 615 * <p> 616 * This method copies the contents of the specified source file 617 * to the specified destination file. 618 * The directory holding the destination file is created if it does not exist. 619 * If the destination file exists, then this method will overwrite it. 620 * 621 * @param srcFile an existing file to copy, must not be <code>null</code> 622 * @param destFile the new file, must not be <code>null</code> 623 * @param preserveFileDate true if the file date of the copy 624 * should be the same as the original 625 * 626 * @throws NullPointerException if source or destination is <code>null</code> 627 * @throws IOException if source or destination is invalid 628 * @throws IOException if an IO error occurs during copying 629 * @see #copyFileToDirectory(File, File, boolean) 630 */ copyFile(File srcFile, File destFile, boolean preserveFileDate)631 public static void copyFile(File srcFile, File destFile, 632 boolean preserveFileDate) throws IOException { 633 if (srcFile == null) { 634 throw new NullPointerException("Source must not be null"); 635 } 636 if (destFile == null) { 637 throw new NullPointerException("Destination must not be null"); 638 } 639 if (srcFile.exists() == false) { 640 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 641 } 642 if (srcFile.isDirectory()) { 643 throw new IOException("Source '" + srcFile + "' exists but is a directory"); 644 } 645 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { 646 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); 647 } 648 if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) { 649 if (destFile.getParentFile().mkdirs() == false) { 650 throw new IOException("Destination '" + destFile + "' directory cannot be created"); 651 } 652 } 653 if (destFile.exists() && destFile.canWrite() == false) { 654 throw new IOException("Destination '" + destFile + "' exists but is read-only"); 655 } 656 doCopyFile(srcFile, destFile, preserveFileDate); 657 } 658 659 /** 660 * Internal copy file method. 661 * 662 * @param srcFile the validated source file, must not be <code>null</code> 663 * @param destFile the validated destination file, must not be <code>null</code> 664 * @param preserveFileDate whether to preserve the file date 665 * @throws IOException if an error occurs 666 */ doCopyFile(File srcFile, File destFile, boolean preserveFileDate)667 private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException { 668 if (destFile.exists() && destFile.isDirectory()) { 669 throw new IOException("Destination '" + destFile + "' exists but is a directory"); 670 } 671 672 FileInputStream input = new FileInputStream(srcFile); 673 try { 674 FileOutputStream output = new FileOutputStream(destFile); 675 try { 676 IOUtils.copy(input, output); 677 } finally { 678 IOUtils.closeQuietly(output); 679 } 680 } finally { 681 IOUtils.closeQuietly(input); 682 } 683 684 if (srcFile.length() != destFile.length()) { 685 throw new IOException("Failed to copy full contents from '" + 686 srcFile + "' to '" + destFile + "'"); 687 } 688 if (preserveFileDate) { 689 destFile.setLastModified(srcFile.lastModified()); 690 } 691 } 692 693 //----------------------------------------------------------------------- 694 /** 695 * Copies a directory to within another directory preserving the file dates. 696 * <p> 697 * This method copies the source directory and all its contents to a 698 * directory of the same name in the specified destination directory. 699 * <p> 700 * The destination directory is created if it does not exist. 701 * If the destination directory did exist, then this method merges 702 * the source with the destination, with the source taking precedence. 703 * 704 * @param srcDir an existing directory to copy, must not be <code>null</code> 705 * @param destDir the directory to place the copy in, must not be <code>null</code> 706 * 707 * @throws NullPointerException if source or destination is <code>null</code> 708 * @throws IOException if source or destination is invalid 709 * @throws IOException if an IO error occurs during copying 710 * @since Commons IO 1.2 711 */ copyDirectoryToDirectory(File srcDir, File destDir)712 public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException { 713 if (srcDir == null) { 714 throw new NullPointerException("Source must not be null"); 715 } 716 if (srcDir.exists() && srcDir.isDirectory() == false) { 717 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory"); 718 } 719 if (destDir == null) { 720 throw new NullPointerException("Destination must not be null"); 721 } 722 if (destDir.exists() && destDir.isDirectory() == false) { 723 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 724 } 725 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true); 726 } 727 728 /** 729 * Copies a whole directory to a new location preserving the file dates. 730 * <p> 731 * This method copies the specified directory and all its child 732 * directories and files to the specified destination. 733 * The destination is the new location and name of the directory. 734 * <p> 735 * The destination directory is created if it does not exist. 736 * If the destination directory did exist, then this method merges 737 * the source with the destination, with the source taking precedence. 738 * 739 * @param srcDir an existing directory to copy, must not be <code>null</code> 740 * @param destDir the new directory, must not be <code>null</code> 741 * 742 * @throws NullPointerException if source or destination is <code>null</code> 743 * @throws IOException if source or destination is invalid 744 * @throws IOException if an IO error occurs during copying 745 * @since Commons IO 1.1 746 */ 747 @VisibleForTesting copyDirectory(File srcDir, File destDir)748 public static void copyDirectory(File srcDir, File destDir) throws IOException { 749 copyDirectory(srcDir, destDir, true); 750 } 751 752 /** 753 * Copies a whole directory to a new location. 754 * <p> 755 * This method copies the contents of the specified source directory 756 * to within the specified destination directory. 757 * <p> 758 * The destination directory is created if it does not exist. 759 * If the destination directory did exist, then this method merges 760 * the source with the destination, with the source taking precedence. 761 * 762 * @param srcDir an existing directory to copy, must not be <code>null</code> 763 * @param destDir the new directory, must not be <code>null</code> 764 * @param preserveFileDate true if the file date of the copy 765 * should be the same as the original 766 * 767 * @throws NullPointerException if source or destination is <code>null</code> 768 * @throws IOException if source or destination is invalid 769 * @throws IOException if an IO error occurs during copying 770 * @since Commons IO 1.1 771 */ copyDirectory(File srcDir, File destDir, boolean preserveFileDate)772 public static void copyDirectory(File srcDir, File destDir, 773 boolean preserveFileDate) throws IOException { 774 copyDirectory(srcDir, destDir, null, preserveFileDate); 775 } 776 777 /** 778 * Copies a filtered directory to a new location preserving the file dates. 779 * <p> 780 * This method copies the contents of the specified source directory 781 * to within the specified destination directory. 782 * <p> 783 * The destination directory is created if it does not exist. 784 * If the destination directory did exist, then this method merges 785 * the source with the destination, with the source taking precedence. 786 * 787 * <h4>Example: Copy directories only</h4> 788 * <pre> 789 * // only copy the directory structure 790 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY); 791 * </pre> 792 * 793 * <h4>Example: Copy directories and txt files</h4> 794 * <pre> 795 * // Create a filter for ".txt" files 796 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 797 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 798 * 799 * // Create a filter for either directories or ".txt" files 800 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 801 * 802 * // Copy using the filter 803 * FileUtils.copyDirectory(srcDir, destDir, filter); 804 * </pre> 805 * 806 * @param srcDir an existing directory to copy, must not be <code>null</code> 807 * @param destDir the new directory, must not be <code>null</code> 808 * @param filter the filter to apply, null means copy all directories and files 809 * should be the same as the original 810 * 811 * @throws NullPointerException if source or destination is <code>null</code> 812 * @throws IOException if source or destination is invalid 813 * @throws IOException if an IO error occurs during copying 814 * @since Commons IO 1.4 815 */ copyDirectory(File srcDir, File destDir, FileFilter filter)816 public static void copyDirectory(File srcDir, File destDir, 817 FileFilter filter) throws IOException { 818 copyDirectory(srcDir, destDir, filter, true); 819 } 820 821 /** 822 * Copies a filtered directory to a new location. 823 * <p> 824 * This method copies the contents of the specified source directory 825 * to within the specified destination directory. 826 * <p> 827 * The destination directory is created if it does not exist. 828 * If the destination directory did exist, then this method merges 829 * the source with the destination, with the source taking precedence. 830 * 831 * <h4>Example: Copy directories only</h4> 832 * <pre> 833 * // only copy the directory structure 834 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false); 835 * </pre> 836 * 837 * <h4>Example: Copy directories and txt files</h4> 838 * <pre> 839 * // Create a filter for ".txt" files 840 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 841 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 842 * 843 * // Create a filter for either directories or ".txt" files 844 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 845 * 846 * // Copy using the filter 847 * FileUtils.copyDirectory(srcDir, destDir, filter, false); 848 * </pre> 849 * 850 * @param srcDir an existing directory to copy, must not be <code>null</code> 851 * @param destDir the new directory, must not be <code>null</code> 852 * @param filter the filter to apply, null means copy all directories and files 853 * @param preserveFileDate true if the file date of the copy 854 * should be the same as the original 855 * 856 * @throws NullPointerException if source or destination is <code>null</code> 857 * @throws IOException if source or destination is invalid 858 * @throws IOException if an IO error occurs during copying 859 * @since Commons IO 1.4 860 */ copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate)861 public static void copyDirectory(File srcDir, File destDir, 862 FileFilter filter, boolean preserveFileDate) throws IOException { 863 if (srcDir == null) { 864 throw new NullPointerException("Source must not be null"); 865 } 866 if (destDir == null) { 867 throw new NullPointerException("Destination must not be null"); 868 } 869 if (srcDir.exists() == false) { 870 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 871 } 872 if (srcDir.isDirectory() == false) { 873 throw new IOException("Source '" + srcDir + "' exists but is not a directory"); 874 } 875 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) { 876 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); 877 } 878 879 // Cater for destination being directory within the source directory (see IO-141) 880 List<String> exclusionList = null; 881 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) { 882 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 883 if (srcFiles != null && srcFiles.length > 0) { 884 exclusionList = new ArrayList<String>(srcFiles.length); 885 for (int i = 0; i < srcFiles.length; i++) { 886 File copiedFile = new File(destDir, srcFiles[i].getName()); 887 exclusionList.add(copiedFile.getCanonicalPath()); 888 } 889 } 890 } 891 doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList); 892 } 893 894 /** 895 * Internal copy directory method. 896 * 897 * @param srcDir the validated source directory, must not be <code>null</code> 898 * @param destDir the validated destination directory, must not be <code>null</code> 899 * @param filter the filter to apply, null means copy all directories and files 900 * @param preserveFileDate whether to preserve the file date 901 * @param exclusionList List of files and directories to exclude from the copy, may be null 902 * @throws IOException if an error occurs 903 * @since Commons IO 1.1 904 */ doCopyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate, List<String> exclusionList)905 private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter, 906 boolean preserveFileDate, List<String> exclusionList) throws IOException { 907 if (destDir.exists()) { 908 if (destDir.isDirectory() == false) { 909 throw new IOException("Destination '" + destDir + "' exists but is not a directory"); 910 } 911 } else { 912 if (destDir.mkdirs() == false) { 913 throw new IOException("Destination '" + destDir + "' directory cannot be created"); 914 } 915 if (preserveFileDate) { 916 destDir.setLastModified(srcDir.lastModified()); 917 } 918 } 919 if (destDir.canWrite() == false) { 920 throw new IOException("Destination '" + destDir + "' cannot be written to"); 921 } 922 // recurse 923 File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 924 if (files == null) { // null if security restricted 925 throw new IOException("Failed to list contents of " + srcDir); 926 } 927 for (int i = 0; i < files.length; i++) { 928 File copiedFile = new File(destDir, files[i].getName()); 929 if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) { 930 if (files[i].isDirectory()) { 931 doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList); 932 } else { 933 doCopyFile(files[i], copiedFile, preserveFileDate); 934 } 935 } 936 } 937 } 938 939 //----------------------------------------------------------------------- 940 /** 941 * Copies bytes from the URL <code>source</code> to a file 942 * <code>destination</code>. The directories up to <code>destination</code> 943 * will be created if they don't already exist. <code>destination</code> 944 * will be overwritten if it already exists. 945 * 946 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code> 947 * @param destination the non-directory <code>File</code> to write bytes to 948 * (possibly overwriting), must not be <code>null</code> 949 * @throws IOException if <code>source</code> URL cannot be opened 950 * @throws IOException if <code>destination</code> is a directory 951 * @throws IOException if <code>destination</code> cannot be written 952 * @throws IOException if <code>destination</code> needs creating but can't be 953 * @throws IOException if an IO error occurs during copying 954 */ copyURLToFile(URL source, File destination)955 public static void copyURLToFile(URL source, File destination) throws IOException { 956 InputStream input = source.openStream(); 957 try { 958 FileOutputStream output = openOutputStream(destination); 959 try { 960 IOUtils.copy(input, output); 961 } finally { 962 IOUtils.closeQuietly(output); 963 } 964 } finally { 965 IOUtils.closeQuietly(input); 966 } 967 } 968 969 //----------------------------------------------------------------------- 970 /** 971 * Deletes a directory recursively. 972 * 973 * @param directory directory to delete 974 * @throws IOException in case deletion is unsuccessful 975 */ deleteDirectory(File directory)976 public static void deleteDirectory(File directory) throws IOException { 977 if (!directory.exists()) { 978 return; 979 } 980 981 cleanDirectory(directory); 982 if (!directory.delete()) { 983 String message = 984 "Unable to delete directory " + directory + "."; 985 throw new IOException(message); 986 } 987 } 988 989 /** 990 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. 991 * <p> 992 * The difference between File.delete() and this method are: 993 * <ul> 994 * <li>A directory to be deleted does not have to be empty.</li> 995 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li> 996 * </ul> 997 * 998 * @param file file or directory to delete, can be <code>null</code> 999 * @return <code>true</code> if the file or directory was deleted, otherwise 1000 * <code>false</code> 1001 * 1002 * @since Commons IO 1.4 1003 */ deleteQuietly(File file)1004 public static boolean deleteQuietly(File file) { 1005 if (file == null) { 1006 return false; 1007 } 1008 try { 1009 if (file.isDirectory()) { 1010 cleanDirectory(file); 1011 } 1012 } catch (Exception e) { 1013 } 1014 1015 try { 1016 return file.delete(); 1017 } catch (Exception e) { 1018 return false; 1019 } 1020 } 1021 1022 /** 1023 * Cleans a directory without deleting it. 1024 * 1025 * @param directory directory to clean 1026 * @throws IOException in case cleaning is unsuccessful 1027 */ cleanDirectory(File directory)1028 public static void cleanDirectory(File directory) throws IOException { 1029 if (!directory.exists()) { 1030 String message = directory + " does not exist"; 1031 throw new IllegalArgumentException(message); 1032 } 1033 1034 if (!directory.isDirectory()) { 1035 String message = directory + " is not a directory"; 1036 throw new IllegalArgumentException(message); 1037 } 1038 1039 File[] files = directory.listFiles(); 1040 if (files == null) { // null if security restricted 1041 throw new IOException("Failed to list contents of " + directory); 1042 } 1043 1044 IOException exception = null; 1045 for (int i = 0; i < files.length; i++) { 1046 File file = files[i]; 1047 try { 1048 forceDelete(file); 1049 } catch (IOException ioe) { 1050 exception = ioe; 1051 } 1052 } 1053 1054 if (null != exception) { 1055 throw exception; 1056 } 1057 } 1058 1059 //----------------------------------------------------------------------- 1060 /** 1061 * Waits for NFS to propagate a file creation, imposing a timeout. 1062 * <p> 1063 * This method repeatedly tests {@link File#exists()} until it returns 1064 * true up to the maximum time specified in seconds. 1065 * 1066 * @param file the file to check, must not be <code>null</code> 1067 * @param seconds the maximum time in seconds to wait 1068 * @return true if file exists 1069 * @throws NullPointerException if the file is <code>null</code> 1070 */ waitFor(File file, int seconds)1071 public static boolean waitFor(File file, int seconds) { 1072 int timeout = 0; 1073 int tick = 0; 1074 while (!file.exists()) { 1075 if (tick++ >= 10) { 1076 tick = 0; 1077 if (timeout++ > seconds) { 1078 return false; 1079 } 1080 } 1081 try { 1082 Thread.sleep(100); 1083 } catch (InterruptedException ignore) { 1084 // ignore exception 1085 } catch (Exception ex) { 1086 break; 1087 } 1088 } 1089 return true; 1090 } 1091 1092 //----------------------------------------------------------------------- 1093 /** 1094 * Reads the contents of a file into a String. 1095 * The file is always closed. 1096 * 1097 * @param file the file to read, must not be <code>null</code> 1098 * @param encoding the encoding to use, <code>null</code> means platform default 1099 * @return the file contents, never <code>null</code> 1100 * @throws IOException in case of an I/O error 1101 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1102 */ readFileToString(File file, String encoding)1103 public static String readFileToString(File file, String encoding) throws IOException { 1104 InputStream in = null; 1105 try { 1106 in = openInputStream(file); 1107 return IOUtils.toString(in, encoding); 1108 } finally { 1109 IOUtils.closeQuietly(in); 1110 } 1111 } 1112 1113 1114 /** 1115 * Reads the contents of a file into a String using the default encoding for the VM. 1116 * The file is always closed. 1117 * 1118 * @param file the file to read, must not be <code>null</code> 1119 * @return the file contents, never <code>null</code> 1120 * @throws IOException in case of an I/O error 1121 * @since Commons IO 1.3.1 1122 */ readFileToString(File file)1123 public static String readFileToString(File file) throws IOException { 1124 return readFileToString(file, null); 1125 } 1126 1127 /** 1128 * Reads the contents of a file into a byte array. 1129 * The file is always closed. 1130 * 1131 * @param file the file to read, must not be <code>null</code> 1132 * @return the file contents, never <code>null</code> 1133 * @throws IOException in case of an I/O error 1134 * @since Commons IO 1.1 1135 */ readFileToByteArray(File file)1136 public static byte[] readFileToByteArray(File file) throws IOException { 1137 InputStream in = null; 1138 try { 1139 in = openInputStream(file); 1140 return IOUtils.toByteArray(in); 1141 } finally { 1142 IOUtils.closeQuietly(in); 1143 } 1144 } 1145 1146 /** 1147 * Reads the contents of a file line by line to a List of Strings. 1148 * The file is always closed. 1149 * 1150 * @param file the file to read, must not be <code>null</code> 1151 * @param encoding the encoding to use, <code>null</code> means platform default 1152 * @return the list of Strings representing each line in the file, never <code>null</code> 1153 * @throws IOException in case of an I/O error 1154 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1155 * @since Commons IO 1.1 1156 */ readLines(File file, String encoding)1157 public static List<String> readLines(File file, String encoding) throws IOException { 1158 InputStream in = null; 1159 try { 1160 in = openInputStream(file); 1161 return IOUtils.readLines(in, encoding); 1162 } finally { 1163 IOUtils.closeQuietly(in); 1164 } 1165 } 1166 1167 /** 1168 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. 1169 * The file is always closed. 1170 * 1171 * @param file the file to read, must not be <code>null</code> 1172 * @return the list of Strings representing each line in the file, never <code>null</code> 1173 * @throws IOException in case of an I/O error 1174 * @since Commons IO 1.3 1175 */ readLines(File file)1176 public static List<String> readLines(File file) throws IOException { 1177 return readLines(file, null); 1178 } 1179 1180 /** 1181 * Returns an Iterator for the lines in a <code>File</code>. 1182 * <p> 1183 * This method opens an <code>InputStream</code> for the file. 1184 * When you have finished with the iterator you should close the stream 1185 * to free internal resources. This can be done by calling the 1186 * {@link LineIterator#close()} or 1187 * {@link LineIterator#closeQuietly(LineIterator)} method. 1188 * <p> 1189 * The recommended usage pattern is: 1190 * <pre> 1191 * LineIterator it = FileUtils.lineIterator(file, "UTF-8"); 1192 * try { 1193 * while (it.hasNext()) { 1194 * String line = it.nextLine(); 1195 * /// do something with line 1196 * } 1197 * } finally { 1198 * LineIterator.closeQuietly(iterator); 1199 * } 1200 * </pre> 1201 * <p> 1202 * If an exception occurs during the creation of the iterator, the 1203 * underlying stream is closed. 1204 * 1205 * @param file the file to open for input, must not be <code>null</code> 1206 * @param encoding the encoding to use, <code>null</code> means platform default 1207 * @return an Iterator of the lines in the file, never <code>null</code> 1208 * @throws IOException in case of an I/O error (file closed) 1209 * @since Commons IO 1.2 1210 */ lineIterator(File file, String encoding)1211 public static LineIterator lineIterator(File file, String encoding) throws IOException { 1212 InputStream in = null; 1213 try { 1214 in = openInputStream(file); 1215 return IOUtils.lineIterator(in, encoding); 1216 } catch (IOException ex) { 1217 IOUtils.closeQuietly(in); 1218 throw ex; 1219 } catch (RuntimeException ex) { 1220 IOUtils.closeQuietly(in); 1221 throw ex; 1222 } 1223 } 1224 1225 /** 1226 * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM. 1227 * 1228 * @param file the file to open for input, must not be <code>null</code> 1229 * @return an Iterator of the lines in the file, never <code>null</code> 1230 * @throws IOException in case of an I/O error (file closed) 1231 * @since Commons IO 1.3 1232 * @see #lineIterator(File, String) 1233 */ lineIterator(File file)1234 public static LineIterator lineIterator(File file) throws IOException { 1235 return lineIterator(file, null); 1236 } 1237 1238 //----------------------------------------------------------------------- 1239 /** 1240 * Writes a String to a file creating the file if it does not exist. 1241 * 1242 * NOTE: As from v1.3, the parent directories of the file will be created 1243 * if they do not exist. 1244 * 1245 * @param file the file to write 1246 * @param data the content to write to the file 1247 * @param encoding the encoding to use, <code>null</code> means platform default 1248 * @throws IOException in case of an I/O error 1249 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1250 */ writeStringToFile(File file, String data, String encoding)1251 public static void writeStringToFile(File file, String data, String encoding) throws IOException { 1252 OutputStream out = null; 1253 try { 1254 out = openOutputStream(file); 1255 IOUtils.write(data, out, encoding); 1256 } finally { 1257 IOUtils.closeQuietly(out); 1258 } 1259 } 1260 1261 /** 1262 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1263 * 1264 * @param file the file to write 1265 * @param data the content to write to the file 1266 * @throws IOException in case of an I/O error 1267 */ writeStringToFile(File file, String data)1268 public static void writeStringToFile(File file, String data) throws IOException { 1269 writeStringToFile(file, data, null); 1270 } 1271 1272 /** 1273 * Writes a byte array to a file creating the file if it does not exist. 1274 * <p> 1275 * NOTE: As from v1.3, the parent directories of the file will be created 1276 * if they do not exist. 1277 * 1278 * @param file the file to write to 1279 * @param data the content to write to the file 1280 * @throws IOException in case of an I/O error 1281 * @since Commons IO 1.1 1282 */ writeByteArrayToFile(File file, byte[] data)1283 public static void writeByteArrayToFile(File file, byte[] data) throws IOException { 1284 OutputStream out = null; 1285 try { 1286 out = openOutputStream(file); 1287 out.write(data); 1288 } finally { 1289 IOUtils.closeQuietly(out); 1290 } 1291 } 1292 1293 /** 1294 * Writes the <code>toString()</code> value of each item in a collection to 1295 * the specified <code>File</code> line by line. 1296 * The specified character encoding and the default line ending will be used. 1297 * <p> 1298 * NOTE: As from v1.3, the parent directories of the file will be created 1299 * if they do not exist. 1300 * 1301 * @param file the file to write to 1302 * @param encoding the encoding to use, <code>null</code> means platform default 1303 * @param lines the lines to write, <code>null</code> entries produce blank lines 1304 * @throws IOException in case of an I/O error 1305 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1306 * @since Commons IO 1.1 1307 */ writeLines(File file, String encoding, Collection<Object> lines)1308 public static void writeLines(File file, String encoding, Collection<Object> lines) throws IOException { 1309 writeLines(file, encoding, lines, null); 1310 } 1311 1312 /** 1313 * Writes the <code>toString()</code> value of each item in a collection to 1314 * the specified <code>File</code> line by line. 1315 * The default VM encoding and the default line ending will be used. 1316 * 1317 * @param file the file to write to 1318 * @param lines the lines to write, <code>null</code> entries produce blank lines 1319 * @throws IOException in case of an I/O error 1320 * @since Commons IO 1.3 1321 */ writeLines(File file, Collection<Object> lines)1322 public static void writeLines(File file, Collection<Object> lines) throws IOException { 1323 writeLines(file, null, lines, null); 1324 } 1325 1326 /** 1327 * Writes the <code>toString()</code> value of each item in a collection to 1328 * the specified <code>File</code> line by line. 1329 * The specified character encoding and the line ending will be used. 1330 * <p> 1331 * NOTE: As from v1.3, the parent directories of the file will be created 1332 * if they do not exist. 1333 * 1334 * @param file the file to write to 1335 * @param encoding the encoding to use, <code>null</code> means platform default 1336 * @param lines the lines to write, <code>null</code> entries produce blank lines 1337 * @param lineEnding the line separator to use, <code>null</code> is system default 1338 * @throws IOException in case of an I/O error 1339 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1340 * @since Commons IO 1.1 1341 */ writeLines(File file, String encoding, Collection<Object> lines, String lineEnding)1342 public static void writeLines(File file, String encoding, Collection<Object> lines, String lineEnding) throws IOException { 1343 OutputStream out = null; 1344 try { 1345 out = openOutputStream(file); 1346 IOUtils.writeLines(lines, lineEnding, out, encoding); 1347 } finally { 1348 IOUtils.closeQuietly(out); 1349 } 1350 } 1351 1352 /** 1353 * Writes the <code>toString()</code> value of each item in a collection to 1354 * the specified <code>File</code> line by line. 1355 * The default VM encoding and the specified line ending will be used. 1356 * 1357 * @param file the file to write to 1358 * @param lines the lines to write, <code>null</code> entries produce blank lines 1359 * @param lineEnding the line separator to use, <code>null</code> is system default 1360 * @throws IOException in case of an I/O error 1361 * @since Commons IO 1.3 1362 */ writeLines(File file, Collection<Object> lines, String lineEnding)1363 public static void writeLines(File file, Collection<Object> lines, String lineEnding) throws IOException { 1364 writeLines(file, null, lines, lineEnding); 1365 } 1366 1367 //----------------------------------------------------------------------- 1368 /** 1369 * Deletes a file. If file is a directory, delete it and all sub-directories. 1370 * <p> 1371 * The difference between File.delete() and this method are: 1372 * <ul> 1373 * <li>A directory to be deleted does not have to be empty.</li> 1374 * <li>You get exceptions when a file or directory cannot be deleted. 1375 * (java.io.File methods returns a boolean)</li> 1376 * </ul> 1377 * 1378 * @param file file or directory to delete, must not be <code>null</code> 1379 * @throws NullPointerException if the directory is <code>null</code> 1380 * @throws FileNotFoundException if the file was not found 1381 * @throws IOException in case deletion is unsuccessful 1382 */ forceDelete(File file)1383 public static void forceDelete(File file) throws IOException { 1384 if (file.isDirectory()) { 1385 deleteDirectory(file); 1386 } else { 1387 boolean filePresent = file.exists(); 1388 if (!file.delete()) { 1389 if (!filePresent){ 1390 throw new FileNotFoundException("File does not exist: " + file); 1391 } 1392 String message = 1393 "Unable to delete file: " + file; 1394 throw new IOException(message); 1395 } 1396 } 1397 } 1398 1399 /** 1400 * Schedules a file to be deleted when JVM exits. 1401 * If file is directory delete it and all sub-directories. 1402 * 1403 * @param file file or directory to delete, must not be <code>null</code> 1404 * @throws NullPointerException if the file is <code>null</code> 1405 * @throws IOException in case deletion is unsuccessful 1406 */ forceDeleteOnExit(File file)1407 public static void forceDeleteOnExit(File file) throws IOException { 1408 if (file.isDirectory()) { 1409 deleteDirectoryOnExit(file); 1410 } else { 1411 file.deleteOnExit(); 1412 } 1413 } 1414 1415 /** 1416 * Schedules a directory recursively for deletion on JVM exit. 1417 * 1418 * @param directory directory to delete, must not be <code>null</code> 1419 * @throws NullPointerException if the directory is <code>null</code> 1420 * @throws IOException in case deletion is unsuccessful 1421 */ deleteDirectoryOnExit(File directory)1422 private static void deleteDirectoryOnExit(File directory) throws IOException { 1423 if (!directory.exists()) { 1424 return; 1425 } 1426 1427 cleanDirectoryOnExit(directory); 1428 directory.deleteOnExit(); 1429 } 1430 1431 /** 1432 * Cleans a directory without deleting it. 1433 * 1434 * @param directory directory to clean, must not be <code>null</code> 1435 * @throws NullPointerException if the directory is <code>null</code> 1436 * @throws IOException in case cleaning is unsuccessful 1437 */ cleanDirectoryOnExit(File directory)1438 private static void cleanDirectoryOnExit(File directory) throws IOException { 1439 if (!directory.exists()) { 1440 String message = directory + " does not exist"; 1441 throw new IllegalArgumentException(message); 1442 } 1443 1444 if (!directory.isDirectory()) { 1445 String message = directory + " is not a directory"; 1446 throw new IllegalArgumentException(message); 1447 } 1448 1449 File[] files = directory.listFiles(); 1450 if (files == null) { // null if security restricted 1451 throw new IOException("Failed to list contents of " + directory); 1452 } 1453 1454 IOException exception = null; 1455 for (int i = 0; i < files.length; i++) { 1456 File file = files[i]; 1457 try { 1458 forceDeleteOnExit(file); 1459 } catch (IOException ioe) { 1460 exception = ioe; 1461 } 1462 } 1463 1464 if (null != exception) { 1465 throw exception; 1466 } 1467 } 1468 1469 /** 1470 * Makes a directory, including any necessary but nonexistent parent 1471 * directories. If there already exists a file with specified name or 1472 * the directory cannot be created then an exception is thrown. 1473 * 1474 * @param directory directory to create, must not be <code>null</code> 1475 * @throws NullPointerException if the directory is <code>null</code> 1476 * @throws IOException if the directory cannot be created 1477 */ forceMkdir(File directory)1478 public static void forceMkdir(File directory) throws IOException { 1479 if (directory.exists()) { 1480 if (directory.isFile()) { 1481 String message = 1482 "File " 1483 + directory 1484 + " exists and is " 1485 + "not a directory. Unable to create directory."; 1486 throw new IOException(message); 1487 } 1488 } else { 1489 if (!directory.mkdirs()) { 1490 String message = 1491 "Unable to create directory " + directory; 1492 throw new IOException(message); 1493 } 1494 } 1495 } 1496 1497 //----------------------------------------------------------------------- 1498 /** 1499 * Counts the size of a directory recursively (sum of the length of all files). 1500 * 1501 * @param directory directory to inspect, must not be <code>null</code> 1502 * @return size of directory in bytes, 0 if directory is security restricted 1503 * @throws NullPointerException if the directory is <code>null</code> 1504 */ sizeOfDirectory(File directory)1505 public static long sizeOfDirectory(File directory) { 1506 if (!directory.exists()) { 1507 String message = directory + " does not exist"; 1508 throw new IllegalArgumentException(message); 1509 } 1510 1511 if (!directory.isDirectory()) { 1512 String message = directory + " is not a directory"; 1513 throw new IllegalArgumentException(message); 1514 } 1515 1516 long size = 0; 1517 1518 File[] files = directory.listFiles(); 1519 if (files == null) { // null if security restricted 1520 return 0L; 1521 } 1522 for (int i = 0; i < files.length; i++) { 1523 File file = files[i]; 1524 1525 if (file.isDirectory()) { 1526 size += sizeOfDirectory(file); 1527 } else { 1528 size += file.length(); 1529 } 1530 } 1531 1532 return size; 1533 } 1534 1535 //----------------------------------------------------------------------- 1536 /** 1537 * Tests if the specified <code>File</code> is newer than the reference 1538 * <code>File</code>. 1539 * 1540 * @param file the <code>File</code> of which the modification date must 1541 * be compared, must not be <code>null</code> 1542 * @param reference the <code>File</code> of which the modification date 1543 * is used, must not be <code>null</code> 1544 * @return true if the <code>File</code> exists and has been modified more 1545 * recently than the reference <code>File</code> 1546 * @throws IllegalArgumentException if the file is <code>null</code> 1547 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist 1548 */ isFileNewer(File file, File reference)1549 public static boolean isFileNewer(File file, File reference) { 1550 if (reference == null) { 1551 throw new IllegalArgumentException("No specified reference file"); 1552 } 1553 if (!reference.exists()) { 1554 throw new IllegalArgumentException("The reference file '" 1555 + file + "' doesn't exist"); 1556 } 1557 return isFileNewer(file, reference.lastModified()); 1558 } 1559 1560 /** 1561 * Tests if the specified <code>File</code> is newer than the specified 1562 * <code>Date</code>. 1563 * 1564 * @param file the <code>File</code> of which the modification date 1565 * must be compared, must not be <code>null</code> 1566 * @param date the date reference, must not be <code>null</code> 1567 * @return true if the <code>File</code> exists and has been modified 1568 * after the given <code>Date</code>. 1569 * @throws IllegalArgumentException if the file is <code>null</code> 1570 * @throws IllegalArgumentException if the date is <code>null</code> 1571 */ isFileNewer(File file, Date date)1572 public static boolean isFileNewer(File file, Date date) { 1573 if (date == null) { 1574 throw new IllegalArgumentException("No specified date"); 1575 } 1576 return isFileNewer(file, date.getTime()); 1577 } 1578 1579 /** 1580 * Tests if the specified <code>File</code> is newer than the specified 1581 * time reference. 1582 * 1583 * @param file the <code>File</code> of which the modification date must 1584 * be compared, must not be <code>null</code> 1585 * @param timeMillis the time reference measured in milliseconds since the 1586 * epoch (00:00:00 GMT, January 1, 1970) 1587 * @return true if the <code>File</code> exists and has been modified after 1588 * the given time reference. 1589 * @throws IllegalArgumentException if the file is <code>null</code> 1590 */ isFileNewer(File file, long timeMillis)1591 public static boolean isFileNewer(File file, long timeMillis) { 1592 if (file == null) { 1593 throw new IllegalArgumentException("No specified file"); 1594 } 1595 if (!file.exists()) { 1596 return false; 1597 } 1598 return file.lastModified() > timeMillis; 1599 } 1600 1601 1602 //----------------------------------------------------------------------- 1603 /** 1604 * Tests if the specified <code>File</code> is older than the reference 1605 * <code>File</code>. 1606 * 1607 * @param file the <code>File</code> of which the modification date must 1608 * be compared, must not be <code>null</code> 1609 * @param reference the <code>File</code> of which the modification date 1610 * is used, must not be <code>null</code> 1611 * @return true if the <code>File</code> exists and has been modified before 1612 * the reference <code>File</code> 1613 * @throws IllegalArgumentException if the file is <code>null</code> 1614 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist 1615 */ isFileOlder(File file, File reference)1616 public static boolean isFileOlder(File file, File reference) { 1617 if (reference == null) { 1618 throw new IllegalArgumentException("No specified reference file"); 1619 } 1620 if (!reference.exists()) { 1621 throw new IllegalArgumentException("The reference file '" 1622 + file + "' doesn't exist"); 1623 } 1624 return isFileOlder(file, reference.lastModified()); 1625 } 1626 1627 /** 1628 * Tests if the specified <code>File</code> is older than the specified 1629 * <code>Date</code>. 1630 * 1631 * @param file the <code>File</code> of which the modification date 1632 * must be compared, must not be <code>null</code> 1633 * @param date the date reference, must not be <code>null</code> 1634 * @return true if the <code>File</code> exists and has been modified 1635 * before the given <code>Date</code>. 1636 * @throws IllegalArgumentException if the file is <code>null</code> 1637 * @throws IllegalArgumentException if the date is <code>null</code> 1638 */ isFileOlder(File file, Date date)1639 public static boolean isFileOlder(File file, Date date) { 1640 if (date == null) { 1641 throw new IllegalArgumentException("No specified date"); 1642 } 1643 return isFileOlder(file, date.getTime()); 1644 } 1645 1646 /** 1647 * Tests if the specified <code>File</code> is older than the specified 1648 * time reference. 1649 * 1650 * @param file the <code>File</code> of which the modification date must 1651 * be compared, must not be <code>null</code> 1652 * @param timeMillis the time reference measured in milliseconds since the 1653 * epoch (00:00:00 GMT, January 1, 1970) 1654 * @return true if the <code>File</code> exists and has been modified before 1655 * the given time reference. 1656 * @throws IllegalArgumentException if the file is <code>null</code> 1657 */ isFileOlder(File file, long timeMillis)1658 public static boolean isFileOlder(File file, long timeMillis) { 1659 if (file == null) { 1660 throw new IllegalArgumentException("No specified file"); 1661 } 1662 if (!file.exists()) { 1663 return false; 1664 } 1665 return file.lastModified() < timeMillis; 1666 } 1667 1668 //----------------------------------------------------------------------- 1669 /** 1670 * Computes the checksum of a file using the CRC32 checksum routine. 1671 * The value of the checksum is returned. 1672 * 1673 * @param file the file to checksum, must not be <code>null</code> 1674 * @return the checksum value 1675 * @throws NullPointerException if the file or checksum is <code>null</code> 1676 * @throws IllegalArgumentException if the file is a directory 1677 * @throws IOException if an IO error occurs reading the file 1678 * @since Commons IO 1.3 1679 */ checksumCRC32(File file)1680 public static long checksumCRC32(File file) throws IOException { 1681 CRC32 crc = new CRC32(); 1682 checksum(file, crc); 1683 return crc.getValue(); 1684 } 1685 1686 /** 1687 * Computes the checksum of a file using the specified checksum object. 1688 * Multiple files may be checked using one <code>Checksum</code> instance 1689 * if desired simply by reusing the same checksum object. 1690 * For example: 1691 * <pre> 1692 * long csum = FileUtils.checksum(file, new CRC32()).getValue(); 1693 * </pre> 1694 * 1695 * @param file the file to checksum, must not be <code>null</code> 1696 * @param checksum the checksum object to be used, must not be <code>null</code> 1697 * @return the checksum specified, updated with the content of the file 1698 * @throws NullPointerException if the file or checksum is <code>null</code> 1699 * @throws IllegalArgumentException if the file is a directory 1700 * @throws IOException if an IO error occurs reading the file 1701 * @since Commons IO 1.3 1702 */ checksum(File file, Checksum checksum)1703 public static Checksum checksum(File file, Checksum checksum) throws IOException { 1704 if (file.isDirectory()) { 1705 throw new IllegalArgumentException("Checksums can't be computed on directories"); 1706 } 1707 InputStream in = null; 1708 try { 1709 in = new CheckedInputStream(new FileInputStream(file), checksum); 1710 IOUtils.copy(in, new NullOutputStream()); 1711 } finally { 1712 IOUtils.closeQuietly(in); 1713 } 1714 return checksum; 1715 } 1716 1717 /** 1718 * Moves a directory. 1719 * <p> 1720 * When the destination directory is on another file system, do a "copy and delete". 1721 * 1722 * @param srcDir the directory to be moved 1723 * @param destDir the destination directory 1724 * @throws NullPointerException if source or destination is <code>null</code> 1725 * @throws IOException if source or destination is invalid 1726 * @throws IOException if an IO error occurs moving the file 1727 * @since Commons IO 1.4 1728 */ moveDirectory(File srcDir, File destDir)1729 public static void moveDirectory(File srcDir, File destDir) throws IOException { 1730 if (srcDir == null) { 1731 throw new NullPointerException("Source must not be null"); 1732 } 1733 if (destDir == null) { 1734 throw new NullPointerException("Destination must not be null"); 1735 } 1736 if (!srcDir.exists()) { 1737 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 1738 } 1739 if (!srcDir.isDirectory()) { 1740 throw new IOException("Source '" + srcDir + "' is not a directory"); 1741 } 1742 if (destDir.exists()) { 1743 throw new IOException("Destination '" + destDir + "' already exists"); 1744 } 1745 boolean rename = srcDir.renameTo(destDir); 1746 if (!rename) { 1747 copyDirectory( srcDir, destDir ); 1748 deleteDirectory( srcDir ); 1749 if (srcDir.exists()) { 1750 throw new IOException("Failed to delete original directory '" + srcDir + 1751 "' after copy to '" + destDir + "'"); 1752 } 1753 } 1754 } 1755 1756 /** 1757 * Moves a directory to another directory. 1758 * 1759 * @param src the file to be moved 1760 * @param destDir the destination file 1761 * @param createDestDir If <code>true</code> create the destination directory, 1762 * otherwise if <code>false</code> throw an IOException 1763 * @throws NullPointerException if source or destination is <code>null</code> 1764 * @throws IOException if source or destination is invalid 1765 * @throws IOException if an IO error occurs moving the file 1766 * @since Commons IO 1.4 1767 */ moveDirectoryToDirectory(File src, File destDir, boolean createDestDir)1768 public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException { 1769 if (src == null) { 1770 throw new NullPointerException("Source must not be null"); 1771 } 1772 if (destDir == null) { 1773 throw new NullPointerException("Destination directory must not be null"); 1774 } 1775 if (!destDir.exists() && createDestDir) { 1776 destDir.mkdirs(); 1777 } 1778 if (!destDir.exists()) { 1779 throw new FileNotFoundException("Destination directory '" + destDir + 1780 "' does not exist [createDestDir=" + createDestDir +"]"); 1781 } 1782 if (!destDir.isDirectory()) { 1783 throw new IOException("Destination '" + destDir + "' is not a directory"); 1784 } 1785 moveDirectory(src, new File(destDir, src.getName())); 1786 1787 } 1788 1789 /** 1790 * Moves a file. 1791 * <p> 1792 * When the destination file is on another file system, do a "copy and delete". 1793 * 1794 * @param srcFile the file to be moved 1795 * @param destFile the destination file 1796 * @throws NullPointerException if source or destination is <code>null</code> 1797 * @throws IOException if source or destination is invalid 1798 * @throws IOException if an IO error occurs moving the file 1799 * @since Commons IO 1.4 1800 */ moveFile(File srcFile, File destFile)1801 public static void moveFile(File srcFile, File destFile) throws IOException { 1802 if (srcFile == null) { 1803 throw new NullPointerException("Source must not be null"); 1804 } 1805 if (destFile == null) { 1806 throw new NullPointerException("Destination must not be null"); 1807 } 1808 if (!srcFile.exists()) { 1809 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 1810 } 1811 if (srcFile.isDirectory()) { 1812 throw new IOException("Source '" + srcFile + "' is a directory"); 1813 } 1814 if (destFile.exists()) { 1815 throw new IOException("Destination '" + destFile + "' already exists"); 1816 } 1817 if (destFile.isDirectory()) { 1818 throw new IOException("Destination '" + destFile + "' is a directory"); 1819 } 1820 boolean rename = srcFile.renameTo(destFile); 1821 if (!rename) { 1822 copyFile( srcFile, destFile ); 1823 if (!srcFile.delete()) { 1824 FileUtils.deleteQuietly(destFile); 1825 throw new IOException("Failed to delete original file '" + srcFile + 1826 "' after copy to '" + destFile + "'"); 1827 } 1828 } 1829 } 1830 1831 /** 1832 * Moves a file to a directory. 1833 * 1834 * @param srcFile the file to be moved 1835 * @param destDir the destination file 1836 * @param createDestDir If <code>true</code> create the destination directory, 1837 * otherwise if <code>false</code> throw an IOException 1838 * @throws NullPointerException if source or destination is <code>null</code> 1839 * @throws IOException if source or destination is invalid 1840 * @throws IOException if an IO error occurs moving the file 1841 * @since Commons IO 1.4 1842 */ moveFileToDirectory(File srcFile, File destDir, boolean createDestDir)1843 public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException { 1844 if (srcFile == null) { 1845 throw new NullPointerException("Source must not be null"); 1846 } 1847 if (destDir == null) { 1848 throw new NullPointerException("Destination directory must not be null"); 1849 } 1850 if (!destDir.exists() && createDestDir) { 1851 destDir.mkdirs(); 1852 } 1853 if (!destDir.exists()) { 1854 throw new FileNotFoundException("Destination directory '" + destDir + 1855 "' does not exist [createDestDir=" + createDestDir +"]"); 1856 } 1857 if (!destDir.isDirectory()) { 1858 throw new IOException("Destination '" + destDir + "' is not a directory"); 1859 } 1860 moveFile(srcFile, new File(destDir, srcFile.getName())); 1861 } 1862 1863 /** 1864 * Moves a file or directory to the destination directory. 1865 * <p> 1866 * When the destination is on another file system, do a "copy and delete". 1867 * 1868 * @param src the file or directory to be moved 1869 * @param destDir the destination directory 1870 * @param createDestDir If <code>true</code> create the destination directory, 1871 * otherwise if <code>false</code> throw an IOException 1872 * @throws NullPointerException if source or destination is <code>null</code> 1873 * @throws IOException if source or destination is invalid 1874 * @throws IOException if an IO error occurs moving the file 1875 * @since Commons IO 1.4 1876 */ moveToDirectory(File src, File destDir, boolean createDestDir)1877 public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException { 1878 if (src == null) { 1879 throw new NullPointerException("Source must not be null"); 1880 } 1881 if (destDir == null) { 1882 throw new NullPointerException("Destination must not be null"); 1883 } 1884 if (!src.exists()) { 1885 throw new FileNotFoundException("Source '" + src + "' does not exist"); 1886 } 1887 if (src.isDirectory()) { 1888 moveDirectoryToDirectory(src, destDir, createDestDir); 1889 } else { 1890 moveFileToDirectory(src, destDir, createDestDir); 1891 } 1892 } 1893 1894 } 1895