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