001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.io; 018 019 import java.io.File; 020 import java.io.FileFilter; 021 import java.io.FileInputStream; 022 import java.io.FileNotFoundException; 023 import java.io.FileOutputStream; 024 import java.io.IOException; 025 import java.io.InputStream; 026 import java.io.OutputStream; 027 import java.math.BigInteger; 028 import java.net.URL; 029 import java.net.URLConnection; 030 import java.nio.ByteBuffer; 031 import java.nio.channels.FileChannel; 032 import java.nio.charset.Charset; 033 import java.util.ArrayList; 034 import java.util.Collection; 035 import java.util.Date; 036 import java.util.Iterator; 037 import java.util.List; 038 import java.util.zip.CRC32; 039 import java.util.zip.CheckedInputStream; 040 import java.util.zip.Checksum; 041 042 import org.apache.commons.io.filefilter.DirectoryFileFilter; 043 import org.apache.commons.io.filefilter.FalseFileFilter; 044 import org.apache.commons.io.filefilter.FileFilterUtils; 045 import org.apache.commons.io.filefilter.IOFileFilter; 046 import org.apache.commons.io.filefilter.SuffixFileFilter; 047 import org.apache.commons.io.filefilter.TrueFileFilter; 048 import org.apache.commons.io.output.NullOutputStream; 049 050 /** 051 * General file manipulation utilities. 052 * <p> 053 * Facilities are provided in the following areas: 054 * <ul> 055 * <li>writing to a file 056 * <li>reading from a file 057 * <li>make a directory including parent directories 058 * <li>copying files and directories 059 * <li>deleting files and directories 060 * <li>converting to and from a URL 061 * <li>listing files and directories by filter and extension 062 * <li>comparing file content 063 * <li>file last changed date 064 * <li>calculating a checksum 065 * </ul> 066 * <p> 067 * Origin of code: Excalibur, Alexandria, Commons-Utils 068 * 069 * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A> 070 * @author <a href="mailto:sanders@apache.org">Scott Sanders</a> 071 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a> 072 * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a> 073 * @author <a href="mailto:peter@apache.org">Peter Donald</a> 074 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a> 075 * @author Matthew Hawthorne 076 * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a> 077 * @author Stephen Colebourne 078 * @author Ian Springer 079 * @author Chris Eldredge 080 * @author Jim Harrington 081 * @author Sandy McArthur 082 * @version $Id: FileUtils.java 1163295 2011-08-30 17:22:38Z ggregory $ 083 */ 084 public class FileUtils { 085 086 /** 087 * Instances should NOT be constructed in standard programming. 088 */ 089 public FileUtils() { 090 super(); 091 } 092 093 /** 094 * The number of bytes in a kilobyte. 095 */ 096 public static final long ONE_KB = 1024; 097 098 /** 099 * The number of bytes in a megabyte. 100 */ 101 public static final long ONE_MB = ONE_KB * ONE_KB; 102 103 /** 104 * The file copy buffer size (30 MB) 105 */ 106 private static final long FILE_COPY_BUFFER_SIZE = ONE_MB * 30; 107 108 /** 109 * The number of bytes in a gigabyte. 110 */ 111 public static final long ONE_GB = ONE_KB * ONE_MB; 112 113 /** 114 * The number of bytes in a terabyte. 115 */ 116 public static final long ONE_TB = ONE_KB * ONE_GB; 117 118 /** 119 * The number of bytes in a petabyte. 120 */ 121 public static final long ONE_PB = ONE_KB * ONE_TB; 122 123 /** 124 * The number of bytes in an exabyte. 125 */ 126 public static final long ONE_EB = ONE_KB * ONE_PB; 127 128 /** 129 * The number of bytes in a zettabyte. 130 */ 131 public static final BigInteger ONE_ZB = BigInteger.valueOf(ONE_KB).multiply(BigInteger.valueOf(ONE_EB)); 132 133 /** 134 * The number of bytes in a yottabyte. 135 */ 136 public static final BigInteger ONE_YB = ONE_ZB.multiply(BigInteger.valueOf(ONE_EB)); 137 138 /** 139 * An empty array of type <code>File</code>. 140 */ 141 public static final File[] EMPTY_FILE_ARRAY = new File[0]; 142 143 /** 144 * The UTF-8 character set, used to decode octets in URLs. 145 */ 146 private static final Charset UTF8 = Charset.forName("UTF-8"); 147 148 //----------------------------------------------------------------------- 149 /** 150 * Construct a file from the set of name elements. 151 * 152 * @param directory the parent directory 153 * @param names the name elements 154 * @return the file 155 * @since Commons IO 2.1 156 */ 157 public static File getFile(File directory, String... names) { 158 if (directory == null) { 159 throw new NullPointerException("directorydirectory must not be null"); 160 } 161 if (names == null) { 162 throw new NullPointerException("names must not be null"); 163 } 164 File file = directory; 165 for (String name : names) { 166 file = new File(file, name); 167 } 168 return file; 169 } 170 171 /** 172 * Construct a file from the set of name elements. 173 * 174 * @param names the name elements 175 * @return the file 176 * @since Commons IO 2.1 177 */ 178 public static File getFile(String... names) { 179 if (names == null) { 180 throw new NullPointerException("names must not be null"); 181 } 182 File file = null; 183 for (String name : names) { 184 if (file == null) { 185 file = new File(name); 186 } else { 187 file = new File(file, name); 188 } 189 } 190 return file; 191 } 192 193 /** 194 * Returns the path to the system temporary directory. 195 * 196 * @return the path to the system temporary directory. 197 * 198 * @since Commons IO 2.0 199 */ 200 public static String getTempDirectoryPath() { 201 return System.getProperty("java.io.tmpdir"); 202 } 203 204 /** 205 * Returns a {@link File} representing the system temporary directory. 206 * 207 * @return the system temporary directory. 208 * 209 * @since Commons IO 2.0 210 */ 211 public static File getTempDirectory() { 212 return new File(getTempDirectoryPath()); 213 } 214 215 /** 216 * Returns the path to the user's home directory. 217 * 218 * @return the path to the user's home directory. 219 * 220 * @since Commons IO 2.0 221 */ 222 public static String getUserDirectoryPath() { 223 return System.getProperty("user.home"); 224 } 225 226 /** 227 * Returns a {@link File} representing the user's home directory. 228 * 229 * @return the user's home directory. 230 * 231 * @since Commons IO 2.0 232 */ 233 public static File getUserDirectory() { 234 return new File(getUserDirectoryPath()); 235 } 236 237 //----------------------------------------------------------------------- 238 /** 239 * Opens a {@link FileInputStream} for the specified file, providing better 240 * error messages than simply calling <code>new FileInputStream(file)</code>. 241 * <p> 242 * At the end of the method either the stream will be successfully opened, 243 * or an exception will have been thrown. 244 * <p> 245 * An exception is thrown if the file does not exist. 246 * An exception is thrown if the file object exists but is a directory. 247 * An exception is thrown if the file exists but cannot be read. 248 * 249 * @param file the file to open for input, must not be <code>null</code> 250 * @return a new {@link FileInputStream} for the specified file 251 * @throws FileNotFoundException if the file does not exist 252 * @throws IOException if the file object is a directory 253 * @throws IOException if the file cannot be read 254 * @since Commons IO 1.3 255 */ 256 public static FileInputStream openInputStream(File file) throws IOException { 257 if (file.exists()) { 258 if (file.isDirectory()) { 259 throw new IOException("File '" + file + "' exists but is a directory"); 260 } 261 if (file.canRead() == false) { 262 throw new IOException("File '" + file + "' cannot be read"); 263 } 264 } else { 265 throw new FileNotFoundException("File '" + file + "' does not exist"); 266 } 267 return new FileInputStream(file); 268 } 269 270 //----------------------------------------------------------------------- 271 /** 272 * Opens a {@link FileOutputStream} for the specified file, checking and 273 * creating the parent directory if it does not exist. 274 * <p> 275 * At the end of the method either the stream will be successfully opened, 276 * or an exception will have been thrown. 277 * <p> 278 * The parent directory will be created if it does not exist. 279 * The file will be created if it does not exist. 280 * An exception is thrown if the file object exists but is a directory. 281 * An exception is thrown if the file exists but cannot be written to. 282 * An exception is thrown if the parent directory cannot be created. 283 * 284 * @param file the file to open for output, must not be <code>null</code> 285 * @return a new {@link FileOutputStream} for the specified file 286 * @throws IOException if the file object is a directory 287 * @throws IOException if the file cannot be written to 288 * @throws IOException if a parent directory needs creating but that fails 289 * @since Commons IO 1.3 290 */ 291 public static FileOutputStream openOutputStream(File file) throws IOException { 292 return openOutputStream(file, false); 293 } 294 295 /** 296 * Opens a {@link FileOutputStream} for the specified file, checking and 297 * creating the parent directory if it does not exist. 298 * <p> 299 * At the end of the method either the stream will be successfully opened, 300 * or an exception will have been thrown. 301 * <p> 302 * The parent directory will be created if it does not exist. 303 * The file will be created if it does not exist. 304 * An exception is thrown if the file object exists but is a directory. 305 * An exception is thrown if the file exists but cannot be written to. 306 * An exception is thrown if the parent directory cannot be created. 307 * 308 * @param file the file to open for output, must not be <code>null</code> 309 * @param append if <code>true</code>, then bytes will be added to the 310 * end of the file rather than overwriting 311 * @return a new {@link FileOutputStream} for the specified file 312 * @throws IOException if the file object is a directory 313 * @throws IOException if the file cannot be written to 314 * @throws IOException if a parent directory needs creating but that fails 315 * @since Commons IO 2.1 316 */ 317 public static FileOutputStream openOutputStream(File file, boolean append) throws IOException { 318 if (file.exists()) { 319 if (file.isDirectory()) { 320 throw new IOException("File '" + file + "' exists but is a directory"); 321 } 322 if (file.canWrite() == false) { 323 throw new IOException("File '" + file + "' cannot be written to"); 324 } 325 } else { 326 File parent = file.getParentFile(); 327 if (parent != null) { 328 if (!parent.mkdirs() && !parent.isDirectory()) { 329 throw new IOException("Directory '" + parent + "' could not be created"); 330 } 331 } 332 } 333 return new FileOutputStream(file, append); 334 } 335 336 //----------------------------------------------------------------------- 337 /** 338 * Returns a human-readable version of the file size, where the input 339 * represents a specific number of bytes. 340 * 341 * If the size is over 1GB, the size is returned as the number of whole GB, 342 * i.e. the size is rounded down to the nearest GB boundary. 343 * 344 * Similarly for the 1MB and 1KB boundaries. 345 * 346 * @param size the number of bytes 347 * @return a human-readable display value (includes units - GB, MB, KB or bytes) 348 */ 349 // See https://issues.apache.org/jira/browse/IO-226 - should the rounding be changed? 350 public static String byteCountToDisplaySize(long size) { 351 String displaySize; 352 353 // if (size / ONE_EB > 0) { 354 // displaySize = String.valueOf(size / ONE_EB) + " EB"; 355 // } else if (size / ONE_PB > 0) { 356 // displaySize = String.valueOf(size / ONE_PB) + " PB"; 357 // } else if (size / ONE_TB > 0) { 358 // displaySize = String.valueOf(size / ONE_TB) + " TB"; 359 // } else 360 if (size / ONE_GB > 0) { 361 displaySize = String.valueOf(size / ONE_GB) + " GB"; 362 } else if (size / ONE_MB > 0) { 363 displaySize = String.valueOf(size / ONE_MB) + " MB"; 364 } else if (size / ONE_KB > 0) { 365 displaySize = String.valueOf(size / ONE_KB) + " KB"; 366 } else { 367 displaySize = String.valueOf(size) + " bytes"; 368 } 369 return displaySize; 370 } 371 372 //----------------------------------------------------------------------- 373 /** 374 * Implements the same behaviour as the "touch" utility on Unix. It creates 375 * a new file with size 0 or, if the file exists already, it is opened and 376 * closed without modifying it, but updating the file date and time. 377 * <p> 378 * NOTE: As from v1.3, this method throws an IOException if the last 379 * modified date of the file cannot be set. Also, as from v1.3 this method 380 * creates parent directories if they do not exist. 381 * 382 * @param file the File to touch 383 * @throws IOException If an I/O problem occurs 384 */ 385 public static void touch(File file) throws IOException { 386 if (!file.exists()) { 387 OutputStream out = openOutputStream(file); 388 IOUtils.closeQuietly(out); 389 } 390 boolean success = file.setLastModified(System.currentTimeMillis()); 391 if (!success) { 392 throw new IOException("Unable to set the last modification time for " + file); 393 } 394 } 395 396 //----------------------------------------------------------------------- 397 /** 398 * Converts a Collection containing java.io.File instanced into array 399 * representation. This is to account for the difference between 400 * File.listFiles() and FileUtils.listFiles(). 401 * 402 * @param files a Collection containing java.io.File instances 403 * @return an array of java.io.File 404 */ 405 public static File[] convertFileCollectionToFileArray(Collection<File> files) { 406 return files.toArray(new File[files.size()]); 407 } 408 409 //----------------------------------------------------------------------- 410 /** 411 * Finds files within a given directory (and optionally its 412 * subdirectories). All files found are filtered by an IOFileFilter. 413 * 414 * @param files the collection of files found. 415 * @param directory the directory to search in. 416 * @param filter the filter to apply to files and directories. 417 */ 418 private static void innerListFiles(Collection<File> files, File directory, 419 IOFileFilter filter) { 420 File[] found = directory.listFiles((FileFilter) filter); 421 if (found != null) { 422 for (File file : found) { 423 if (file.isDirectory()) { 424 innerListFiles(files, file, filter); 425 } else { 426 files.add(file); 427 } 428 } 429 } 430 } 431 432 /** 433 * Finds files within a given directory (and optionally its 434 * subdirectories). All files found are filtered by an IOFileFilter. 435 * <p> 436 * If your search should recurse into subdirectories you can pass in 437 * an IOFileFilter for directories. You don't need to bind a 438 * DirectoryFileFilter (via logical AND) to this filter. This method does 439 * that for you. 440 * <p> 441 * An example: If you want to search through all directories called 442 * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code> 443 * <p> 444 * Another common usage of this method is find files in a directory 445 * tree but ignoring the directories generated CVS. You can simply pass 446 * in <code>FileFilterUtils.makeCVSAware(null)</code>. 447 * 448 * @param directory the directory to search in 449 * @param fileFilter filter to apply when finding files. 450 * @param dirFilter optional filter to apply when finding subdirectories. 451 * If this parameter is <code>null</code>, subdirectories will not be included in the 452 * search. Use TrueFileFilter.INSTANCE to match all directories. 453 * @return an collection of java.io.File with the matching files 454 * @see org.apache.commons.io.filefilter.FileFilterUtils 455 * @see org.apache.commons.io.filefilter.NameFileFilter 456 */ 457 public static Collection<File> listFiles( 458 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 459 if (!directory.isDirectory()) { 460 throw new IllegalArgumentException( 461 "Parameter 'directory' is not a directory"); 462 } 463 if (fileFilter == null) { 464 throw new NullPointerException("Parameter 'fileFilter' is null"); 465 } 466 467 //Setup effective file filter 468 IOFileFilter effFileFilter = FileFilterUtils.and(fileFilter, 469 FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE)); 470 471 //Setup effective directory filter 472 IOFileFilter effDirFilter; 473 if (dirFilter == null) { 474 effDirFilter = FalseFileFilter.INSTANCE; 475 } else { 476 effDirFilter = FileFilterUtils.and(dirFilter, 477 DirectoryFileFilter.INSTANCE); 478 } 479 480 //Find files 481 Collection<File> files = new java.util.LinkedList<File>(); 482 innerListFiles(files, directory, 483 FileFilterUtils.or(effFileFilter, effDirFilter)); 484 return files; 485 } 486 487 /** 488 * Allows iteration over the files in given directory (and optionally 489 * its subdirectories). 490 * <p> 491 * All files found are filtered by an IOFileFilter. This method is 492 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}, 493 * which supports Iterable ('foreach' loop). 494 * <p> 495 * @param directory the directory to search in 496 * @param fileFilter filter to apply when finding files. 497 * @param dirFilter optional filter to apply when finding subdirectories. 498 * If this parameter is <code>null</code>, subdirectories will not be included in the 499 * search. Use TrueFileFilter.INSTANCE to match all directories. 500 * @return an iterator of java.io.File for the matching files 501 * @see org.apache.commons.io.filefilter.FileFilterUtils 502 * @see org.apache.commons.io.filefilter.NameFileFilter 503 * @since Commons IO 1.2 504 */ 505 public static Iterator<File> iterateFiles( 506 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) { 507 return listFiles(directory, fileFilter, dirFilter).iterator(); 508 } 509 510 //----------------------------------------------------------------------- 511 /** 512 * Converts an array of file extensions to suffixes for use 513 * with IOFileFilters. 514 * 515 * @param extensions an array of extensions. Format: {"java", "xml"} 516 * @return an array of suffixes. Format: {".java", ".xml"} 517 */ 518 private static String[] toSuffixes(String[] extensions) { 519 String[] suffixes = new String[extensions.length]; 520 for (int i = 0; i < extensions.length; i++) { 521 suffixes[i] = "." + extensions[i]; 522 } 523 return suffixes; 524 } 525 526 527 /** 528 * Finds files within a given directory (and optionally its subdirectories) 529 * which match an array of extensions. 530 * 531 * @param directory the directory to search in 532 * @param extensions an array of extensions, ex. {"java","xml"}. If this 533 * parameter is <code>null</code>, all files are returned. 534 * @param recursive if true all subdirectories are searched as well 535 * @return an collection of java.io.File with the matching files 536 */ 537 public static Collection<File> listFiles( 538 File directory, String[] extensions, boolean recursive) { 539 IOFileFilter filter; 540 if (extensions == null) { 541 filter = TrueFileFilter.INSTANCE; 542 } else { 543 String[] suffixes = toSuffixes(extensions); 544 filter = new SuffixFileFilter(suffixes); 545 } 546 return listFiles(directory, filter, 547 (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE)); 548 } 549 550 /** 551 * Allows iteration over the files in a given directory (and optionally 552 * its subdirectories) which match an array of extensions. This method 553 * is based on {@link #listFiles(File, String[], boolean)}, 554 * which supports Iterable ('foreach' loop). 555 * 556 * @param directory the directory to search in 557 * @param extensions an array of extensions, ex. {"java","xml"}. If this 558 * parameter is <code>null</code>, all files are returned. 559 * @param recursive if true all subdirectories are searched as well 560 * @return an iterator of java.io.File with the matching files 561 * @since Commons IO 1.2 562 */ 563 public static Iterator<File> iterateFiles( 564 File directory, String[] extensions, boolean recursive) { 565 return listFiles(directory, extensions, recursive).iterator(); 566 } 567 568 //----------------------------------------------------------------------- 569 /** 570 * Compares the contents of two files to determine if they are equal or not. 571 * <p> 572 * This method checks to see if the two files are different lengths 573 * or if they point to the same file, before resorting to byte-by-byte 574 * comparison of the contents. 575 * <p> 576 * Code origin: Avalon 577 * 578 * @param file1 the first file 579 * @param file2 the second file 580 * @return true if the content of the files are equal or they both don't 581 * exist, false otherwise 582 * @throws IOException in case of an I/O error 583 */ 584 public static boolean contentEquals(File file1, File file2) throws IOException { 585 boolean file1Exists = file1.exists(); 586 if (file1Exists != file2.exists()) { 587 return false; 588 } 589 590 if (!file1Exists) { 591 // two not existing files are equal 592 return true; 593 } 594 595 if (file1.isDirectory() || file2.isDirectory()) { 596 // don't want to compare directory contents 597 throw new IOException("Can't compare directories, only files"); 598 } 599 600 if (file1.length() != file2.length()) { 601 // lengths differ, cannot be equal 602 return false; 603 } 604 605 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) { 606 // same file 607 return true; 608 } 609 610 InputStream input1 = null; 611 InputStream input2 = null; 612 try { 613 input1 = new FileInputStream(file1); 614 input2 = new FileInputStream(file2); 615 return IOUtils.contentEquals(input1, input2); 616 617 } finally { 618 IOUtils.closeQuietly(input1); 619 IOUtils.closeQuietly(input2); 620 } 621 } 622 623 //----------------------------------------------------------------------- 624 /** 625 * Convert from a <code>URL</code> to a <code>File</code>. 626 * <p> 627 * From version 1.1 this method will decode the URL. 628 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 629 * correctly decoded to <code>/my docs/file.txt</code>. Starting with version 630 * 1.5, this method uses UTF-8 to decode percent-encoded octets to characters. 631 * Additionally, malformed percent-encoded octets are handled leniently by 632 * passing them through literally. 633 * 634 * @param url the file URL to convert, <code>null</code> returns <code>null</code> 635 * @return the equivalent <code>File</code> object, or <code>null</code> 636 * if the URL's protocol is not <code>file</code> 637 */ 638 public static File toFile(URL url) { 639 if (url == null || !"file".equalsIgnoreCase(url.getProtocol())) { 640 return null; 641 } else { 642 String filename = url.getFile().replace('/', File.separatorChar); 643 filename = decodeUrl(filename); 644 return new File(filename); 645 } 646 } 647 648 /** 649 * Decodes the specified URL as per RFC 3986, i.e. transforms 650 * percent-encoded octets to characters by decoding with the UTF-8 character 651 * set. This function is primarily intended for usage with 652 * {@link java.net.URL} which unfortunately does not enforce proper URLs. As 653 * such, this method will leniently accept invalid characters or malformed 654 * percent-encoded octets and simply pass them literally through to the 655 * result string. Except for rare edge cases, this will make unencoded URLs 656 * pass through unaltered. 657 * 658 * @param url The URL to decode, may be <code>null</code>. 659 * @return The decoded URL or <code>null</code> if the input was 660 * <code>null</code>. 661 */ 662 static String decodeUrl(String url) { 663 String decoded = url; 664 if (url != null && url.indexOf('%') >= 0) { 665 int n = url.length(); 666 StringBuffer buffer = new StringBuffer(); 667 ByteBuffer bytes = ByteBuffer.allocate(n); 668 for (int i = 0; i < n;) { 669 if (url.charAt(i) == '%') { 670 try { 671 do { 672 byte octet = (byte) Integer.parseInt(url.substring(i + 1, i + 3), 16); 673 bytes.put(octet); 674 i += 3; 675 } while (i < n && url.charAt(i) == '%'); 676 continue; 677 } catch (RuntimeException e) { 678 // malformed percent-encoded octet, fall through and 679 // append characters literally 680 } finally { 681 if (bytes.position() > 0) { 682 bytes.flip(); 683 buffer.append(UTF8.decode(bytes).toString()); 684 bytes.clear(); 685 } 686 } 687 } 688 buffer.append(url.charAt(i++)); 689 } 690 decoded = buffer.toString(); 691 } 692 return decoded; 693 } 694 695 /** 696 * Converts each of an array of <code>URL</code> to a <code>File</code>. 697 * <p> 698 * Returns an array of the same size as the input. 699 * If the input is <code>null</code>, an empty array is returned. 700 * If the input contains <code>null</code>, the output array contains <code>null</code> at the same 701 * index. 702 * <p> 703 * This method will decode the URL. 704 * Syntax such as <code>file:///my%20docs/file.txt</code> will be 705 * correctly decoded to <code>/my docs/file.txt</code>. 706 * 707 * @param urls the file URLs to convert, <code>null</code> returns empty array 708 * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item 709 * if there was a <code>null</code> at that index in the input array 710 * @throws IllegalArgumentException if any file is not a URL file 711 * @throws IllegalArgumentException if any file is incorrectly encoded 712 * @since Commons IO 1.1 713 */ 714 public static File[] toFiles(URL[] urls) { 715 if (urls == null || urls.length == 0) { 716 return EMPTY_FILE_ARRAY; 717 } 718 File[] files = new File[urls.length]; 719 for (int i = 0; i < urls.length; i++) { 720 URL url = urls[i]; 721 if (url != null) { 722 if (url.getProtocol().equals("file") == false) { 723 throw new IllegalArgumentException( 724 "URL could not be converted to a File: " + url); 725 } 726 files[i] = toFile(url); 727 } 728 } 729 return files; 730 } 731 732 /** 733 * Converts each of an array of <code>File</code> to a <code>URL</code>. 734 * <p> 735 * Returns an array of the same size as the input. 736 * 737 * @param files the files to convert 738 * @return an array of URLs matching the input 739 * @throws IOException if a file cannot be converted 740 */ 741 public static URL[] toURLs(File[] files) throws IOException { 742 URL[] urls = new URL[files.length]; 743 744 for (int i = 0; i < urls.length; i++) { 745 urls[i] = files[i].toURI().toURL(); 746 } 747 748 return urls; 749 } 750 751 //----------------------------------------------------------------------- 752 /** 753 * Copies a file to a directory preserving the file date. 754 * <p> 755 * This method copies the contents of the specified source file 756 * to a file of the same name in the specified destination directory. 757 * The destination directory is created if it does not exist. 758 * If the destination file exists, then this method will overwrite it. 759 * <p> 760 * <strong>Note:</strong> This method tries to preserve the file's last 761 * modified date/times using {@link File#setLastModified(long)}, however 762 * it is not guaranteed that the operation will succeed. 763 * If the modification operation fails, no indication is provided. 764 * 765 * @param srcFile an existing file to copy, must not be <code>null</code> 766 * @param destDir the directory to place the copy in, must not be <code>null</code> 767 * 768 * @throws NullPointerException if source or destination is null 769 * @throws IOException if source or destination is invalid 770 * @throws IOException if an IO error occurs during copying 771 * @see #copyFile(File, File, boolean) 772 */ 773 public static void copyFileToDirectory(File srcFile, File destDir) throws IOException { 774 copyFileToDirectory(srcFile, destDir, true); 775 } 776 777 /** 778 * Copies a file to a directory optionally preserving the file date. 779 * <p> 780 * This method copies the contents of the specified source file 781 * to a file of the same name in the specified destination directory. 782 * The destination directory is created if it does not exist. 783 * If the destination file exists, then this method will overwrite it. 784 * <p> 785 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 786 * <code>true</code> tries to preserve the file's last modified 787 * date/times using {@link File#setLastModified(long)}, however it is 788 * not guaranteed that the operation will succeed. 789 * If the modification operation fails, no indication is provided. 790 * 791 * @param srcFile an existing file to copy, must not be <code>null</code> 792 * @param destDir the directory to place the copy in, must not be <code>null</code> 793 * @param preserveFileDate true if the file date of the copy 794 * should be the same as the original 795 * 796 * @throws NullPointerException if source or destination is <code>null</code> 797 * @throws IOException if source or destination is invalid 798 * @throws IOException if an IO error occurs during copying 799 * @see #copyFile(File, File, boolean) 800 * @since Commons IO 1.3 801 */ 802 public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException { 803 if (destDir == null) { 804 throw new NullPointerException("Destination must not be null"); 805 } 806 if (destDir.exists() && destDir.isDirectory() == false) { 807 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 808 } 809 File destFile = new File(destDir, srcFile.getName()); 810 copyFile(srcFile, destFile, preserveFileDate); 811 } 812 813 /** 814 * Copies a file to a new location preserving the file date. 815 * <p> 816 * This method copies the contents of the specified source file to the 817 * specified destination file. The directory holding the destination file is 818 * created if it does not exist. If the destination file exists, then this 819 * method will overwrite it. 820 * <p> 821 * <strong>Note:</strong> This method tries to preserve the file's last 822 * modified date/times using {@link File#setLastModified(long)}, however 823 * it is not guaranteed that the operation will succeed. 824 * If the modification operation fails, no indication is provided. 825 * 826 * @param srcFile an existing file to copy, must not be <code>null</code> 827 * @param destFile the new file, must not be <code>null</code> 828 * 829 * @throws NullPointerException if source or destination is <code>null</code> 830 * @throws IOException if source or destination is invalid 831 * @throws IOException if an IO error occurs during copying 832 * @see #copyFileToDirectory(File, File) 833 */ 834 public static void copyFile(File srcFile, File destFile) throws IOException { 835 copyFile(srcFile, destFile, true); 836 } 837 838 /** 839 * Copies a file to a new location. 840 * <p> 841 * This method copies the contents of the specified source file 842 * to the specified destination file. 843 * The directory holding the destination file is created if it does not exist. 844 * If the destination file exists, then this method will overwrite it. 845 * <p> 846 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 847 * <code>true</code> tries to preserve the file's last modified 848 * date/times using {@link File#setLastModified(long)}, however it is 849 * not guaranteed that the operation will succeed. 850 * If the modification operation fails, no indication is provided. 851 * 852 * @param srcFile an existing file to copy, must not be <code>null</code> 853 * @param destFile the new file, must not be <code>null</code> 854 * @param preserveFileDate true if the file date of the copy 855 * should be the same as the original 856 * 857 * @throws NullPointerException if source or destination is <code>null</code> 858 * @throws IOException if source or destination is invalid 859 * @throws IOException if an IO error occurs during copying 860 * @see #copyFileToDirectory(File, File, boolean) 861 */ 862 public static void copyFile(File srcFile, File destFile, 863 boolean preserveFileDate) throws IOException { 864 if (srcFile == null) { 865 throw new NullPointerException("Source must not be null"); 866 } 867 if (destFile == null) { 868 throw new NullPointerException("Destination must not be null"); 869 } 870 if (srcFile.exists() == false) { 871 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 872 } 873 if (srcFile.isDirectory()) { 874 throw new IOException("Source '" + srcFile + "' exists but is a directory"); 875 } 876 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) { 877 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same"); 878 } 879 File parentFile = destFile.getParentFile(); 880 if (parentFile != null) { 881 if (!parentFile.mkdirs() && !parentFile.isDirectory()) { 882 throw new IOException("Destination '" + parentFile + "' directory cannot be created"); 883 } 884 } 885 if (destFile.exists() && destFile.canWrite() == false) { 886 throw new IOException("Destination '" + destFile + "' exists but is read-only"); 887 } 888 doCopyFile(srcFile, destFile, preserveFileDate); 889 } 890 891 /** 892 * Copy bytes from a <code>File</code> to an <code>OutputStream</code>. 893 * <p> 894 * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>. 895 * </p> 896 * 897 * @param input 898 * the <code>File</code> to read from 899 * @param output 900 * the <code>OutputStream</code> to write to 901 * @return the number of bytes copied 902 * @throws NullPointerException 903 * if the input or output is null 904 * @throws IOException 905 * if an I/O error occurs 906 * @since Commons IO 2.1 907 */ 908 public static long copyFile(File input, OutputStream output) throws IOException { 909 final FileInputStream fis = new FileInputStream(input); 910 try { 911 return IOUtils.copyLarge(fis, output); 912 } finally { 913 fis.close(); 914 } 915 } 916 917 /** 918 * Internal copy file method. 919 * 920 * @param srcFile the validated source file, must not be <code>null</code> 921 * @param destFile the validated destination file, must not be <code>null</code> 922 * @param preserveFileDate whether to preserve the file date 923 * @throws IOException if an error occurs 924 */ 925 private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException { 926 if (destFile.exists() && destFile.isDirectory()) { 927 throw new IOException("Destination '" + destFile + "' exists but is a directory"); 928 } 929 930 FileInputStream fis = null; 931 FileOutputStream fos = null; 932 FileChannel input = null; 933 FileChannel output = null; 934 try { 935 fis = new FileInputStream(srcFile); 936 fos = new FileOutputStream(destFile); 937 input = fis.getChannel(); 938 output = fos.getChannel(); 939 long size = input.size(); 940 long pos = 0; 941 long count = 0; 942 while (pos < size) { 943 count = (size - pos) > FILE_COPY_BUFFER_SIZE ? FILE_COPY_BUFFER_SIZE : (size - pos); 944 pos += output.transferFrom(input, pos, count); 945 } 946 } finally { 947 IOUtils.closeQuietly(output); 948 IOUtils.closeQuietly(fos); 949 IOUtils.closeQuietly(input); 950 IOUtils.closeQuietly(fis); 951 } 952 953 if (srcFile.length() != destFile.length()) { 954 throw new IOException("Failed to copy full contents from '" + 955 srcFile + "' to '" + destFile + "'"); 956 } 957 if (preserveFileDate) { 958 destFile.setLastModified(srcFile.lastModified()); 959 } 960 } 961 962 //----------------------------------------------------------------------- 963 /** 964 * Copies a directory to within another directory preserving the file dates. 965 * <p> 966 * This method copies the source directory and all its contents to a 967 * directory of the same name in the specified destination directory. 968 * <p> 969 * The destination directory is created if it does not exist. 970 * If the destination directory did exist, then this method merges 971 * the source with the destination, with the source taking precedence. 972 * <p> 973 * <strong>Note:</strong> This method tries to preserve the files' last 974 * modified date/times using {@link File#setLastModified(long)}, however 975 * it is not guaranteed that those operations will succeed. 976 * If the modification operation fails, no indication is provided. 977 * 978 * @param srcDir an existing directory to copy, must not be <code>null</code> 979 * @param destDir the directory to place the copy in, must not be <code>null</code> 980 * 981 * @throws NullPointerException if source or destination is <code>null</code> 982 * @throws IOException if source or destination is invalid 983 * @throws IOException if an IO error occurs during copying 984 * @since Commons IO 1.2 985 */ 986 public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException { 987 if (srcDir == null) { 988 throw new NullPointerException("Source must not be null"); 989 } 990 if (srcDir.exists() && srcDir.isDirectory() == false) { 991 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory"); 992 } 993 if (destDir == null) { 994 throw new NullPointerException("Destination must not be null"); 995 } 996 if (destDir.exists() && destDir.isDirectory() == false) { 997 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory"); 998 } 999 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true); 1000 } 1001 1002 /** 1003 * Copies a whole directory to a new location preserving the file dates. 1004 * <p> 1005 * This method copies the specified directory and all its child 1006 * directories and files to the specified destination. 1007 * The destination is the new location and name of the directory. 1008 * <p> 1009 * The destination directory is created if it does not exist. 1010 * If the destination directory did exist, then this method merges 1011 * the source with the destination, with the source taking precedence. 1012 * <p> 1013 * <strong>Note:</strong> This method tries to preserve the files' last 1014 * modified date/times using {@link File#setLastModified(long)}, however 1015 * it is not guaranteed that those operations will succeed. 1016 * If the modification operation fails, no indication is provided. 1017 * 1018 * @param srcDir an existing directory to copy, must not be <code>null</code> 1019 * @param destDir the new directory, must not be <code>null</code> 1020 * 1021 * @throws NullPointerException if source or destination is <code>null</code> 1022 * @throws IOException if source or destination is invalid 1023 * @throws IOException if an IO error occurs during copying 1024 * @since Commons IO 1.1 1025 */ 1026 public static void copyDirectory(File srcDir, File destDir) throws IOException { 1027 copyDirectory(srcDir, destDir, true); 1028 } 1029 1030 /** 1031 * Copies a whole directory to a new location. 1032 * <p> 1033 * This method copies the contents of the specified source directory 1034 * to within the specified destination directory. 1035 * <p> 1036 * The destination directory is created if it does not exist. 1037 * If the destination directory did exist, then this method merges 1038 * the source with the destination, with the source taking precedence. 1039 * <p> 1040 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1041 * <code>true</code> tries to preserve the files' last modified 1042 * date/times using {@link File#setLastModified(long)}, however it is 1043 * not guaranteed that those operations will succeed. 1044 * If the modification operation fails, no indication is provided. 1045 * 1046 * @param srcDir an existing directory to copy, must not be <code>null</code> 1047 * @param destDir the new directory, must not be <code>null</code> 1048 * @param preserveFileDate true if the file date of the copy 1049 * should be the same as the original 1050 * 1051 * @throws NullPointerException if source or destination is <code>null</code> 1052 * @throws IOException if source or destination is invalid 1053 * @throws IOException if an IO error occurs during copying 1054 * @since Commons IO 1.1 1055 */ 1056 public static void copyDirectory(File srcDir, File destDir, 1057 boolean preserveFileDate) throws IOException { 1058 copyDirectory(srcDir, destDir, null, preserveFileDate); 1059 } 1060 1061 /** 1062 * Copies a filtered directory to a new location preserving the file dates. 1063 * <p> 1064 * This method copies the contents of the specified source directory 1065 * to within the specified destination directory. 1066 * <p> 1067 * The destination directory is created if it does not exist. 1068 * If the destination directory did exist, then this method merges 1069 * the source with the destination, with the source taking precedence. 1070 * <p> 1071 * <strong>Note:</strong> This method tries to preserve the files' last 1072 * modified date/times using {@link File#setLastModified(long)}, however 1073 * it is not guaranteed that those operations will succeed. 1074 * If the modification operation fails, no indication is provided. 1075 * 1076 * <h4>Example: Copy directories only</h4> 1077 * <pre> 1078 * // only copy the directory structure 1079 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY); 1080 * </pre> 1081 * 1082 * <h4>Example: Copy directories and txt files</h4> 1083 * <pre> 1084 * // Create a filter for ".txt" files 1085 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 1086 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 1087 * 1088 * // Create a filter for either directories or ".txt" files 1089 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 1090 * 1091 * // Copy using the filter 1092 * FileUtils.copyDirectory(srcDir, destDir, filter); 1093 * </pre> 1094 * 1095 * @param srcDir an existing directory to copy, must not be <code>null</code> 1096 * @param destDir the new directory, must not be <code>null</code> 1097 * @param filter the filter to apply, null means copy all directories and files 1098 * should be the same as the original 1099 * 1100 * @throws NullPointerException if source or destination is <code>null</code> 1101 * @throws IOException if source or destination is invalid 1102 * @throws IOException if an IO error occurs during copying 1103 * @since Commons IO 1.4 1104 */ 1105 public static void copyDirectory(File srcDir, File destDir, 1106 FileFilter filter) throws IOException { 1107 copyDirectory(srcDir, destDir, filter, true); 1108 } 1109 1110 /** 1111 * Copies a filtered directory to a new location. 1112 * <p> 1113 * This method copies the contents of the specified source directory 1114 * to within the specified destination directory. 1115 * <p> 1116 * The destination directory is created if it does not exist. 1117 * If the destination directory did exist, then this method merges 1118 * the source with the destination, with the source taking precedence. 1119 * <p> 1120 * <strong>Note:</strong> Setting <code>preserveFileDate</code> to 1121 * <code>true</code> tries to preserve the files' last modified 1122 * date/times using {@link File#setLastModified(long)}, however it is 1123 * not guaranteed that those operations will succeed. 1124 * If the modification operation fails, no indication is provided. 1125 * 1126 * <h4>Example: Copy directories only</h4> 1127 * <pre> 1128 * // only copy the directory structure 1129 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false); 1130 * </pre> 1131 * 1132 * <h4>Example: Copy directories and txt files</h4> 1133 * <pre> 1134 * // Create a filter for ".txt" files 1135 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt"); 1136 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter); 1137 * 1138 * // Create a filter for either directories or ".txt" files 1139 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles); 1140 * 1141 * // Copy using the filter 1142 * FileUtils.copyDirectory(srcDir, destDir, filter, false); 1143 * </pre> 1144 * 1145 * @param srcDir an existing directory to copy, must not be <code>null</code> 1146 * @param destDir the new directory, must not be <code>null</code> 1147 * @param filter the filter to apply, null means copy all directories and files 1148 * @param preserveFileDate true if the file date of the copy 1149 * should be the same as the original 1150 * 1151 * @throws NullPointerException if source or destination is <code>null</code> 1152 * @throws IOException if source or destination is invalid 1153 * @throws IOException if an IO error occurs during copying 1154 * @since Commons IO 1.4 1155 */ 1156 public static void copyDirectory(File srcDir, File destDir, 1157 FileFilter filter, boolean preserveFileDate) throws IOException { 1158 if (srcDir == null) { 1159 throw new NullPointerException("Source must not be null"); 1160 } 1161 if (destDir == null) { 1162 throw new NullPointerException("Destination must not be null"); 1163 } 1164 if (srcDir.exists() == false) { 1165 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 1166 } 1167 if (srcDir.isDirectory() == false) { 1168 throw new IOException("Source '" + srcDir + "' exists but is not a directory"); 1169 } 1170 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) { 1171 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same"); 1172 } 1173 1174 // Cater for destination being directory within the source directory (see IO-141) 1175 List<String> exclusionList = null; 1176 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) { 1177 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 1178 if (srcFiles != null && srcFiles.length > 0) { 1179 exclusionList = new ArrayList<String>(srcFiles.length); 1180 for (File srcFile : srcFiles) { 1181 File copiedFile = new File(destDir, srcFile.getName()); 1182 exclusionList.add(copiedFile.getCanonicalPath()); 1183 } 1184 } 1185 } 1186 doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList); 1187 } 1188 1189 /** 1190 * Internal copy directory method. 1191 * 1192 * @param srcDir the validated source directory, must not be <code>null</code> 1193 * @param destDir the validated destination directory, must not be <code>null</code> 1194 * @param filter the filter to apply, null means copy all directories and files 1195 * @param preserveFileDate whether to preserve the file date 1196 * @param exclusionList List of files and directories to exclude from the copy, may be null 1197 * @throws IOException if an error occurs 1198 * @since Commons IO 1.1 1199 */ 1200 private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter, 1201 boolean preserveFileDate, List<String> exclusionList) throws IOException { 1202 // recurse 1203 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter); 1204 if (srcFiles == null) { // null if abstract pathname does not denote a directory, or if an I/O error occurs 1205 throw new IOException("Failed to list contents of " + srcDir); 1206 } 1207 if (destDir.exists()) { 1208 if (destDir.isDirectory() == false) { 1209 throw new IOException("Destination '" + destDir + "' exists but is not a directory"); 1210 } 1211 } else { 1212 if (!destDir.mkdirs() && !destDir.isDirectory()) { 1213 throw new IOException("Destination '" + destDir + "' directory cannot be created"); 1214 } 1215 } 1216 if (destDir.canWrite() == false) { 1217 throw new IOException("Destination '" + destDir + "' cannot be written to"); 1218 } 1219 for (File srcFile : srcFiles) { 1220 File dstFile = new File(destDir, srcFile.getName()); 1221 if (exclusionList == null || !exclusionList.contains(srcFile.getCanonicalPath())) { 1222 if (srcFile.isDirectory()) { 1223 doCopyDirectory(srcFile, dstFile, filter, preserveFileDate, exclusionList); 1224 } else { 1225 doCopyFile(srcFile, dstFile, preserveFileDate); 1226 } 1227 } 1228 } 1229 1230 // Do this last, as the above has probably affected directory metadata 1231 if (preserveFileDate) { 1232 destDir.setLastModified(srcDir.lastModified()); 1233 } 1234 } 1235 1236 //----------------------------------------------------------------------- 1237 /** 1238 * Copies bytes from the URL <code>source</code> to a file 1239 * <code>destination</code>. The directories up to <code>destination</code> 1240 * will be created if they don't already exist. <code>destination</code> 1241 * will be overwritten if it already exists. 1242 * <p> 1243 * Warning: this method does not set a connection or read timeout and thus 1244 * might block forever. Use {@link #copyURLToFile(URL, File, int, int)} 1245 * with reasonable timeouts to prevent this. 1246 * 1247 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code> 1248 * @param destination the non-directory <code>File</code> to write bytes to 1249 * (possibly overwriting), must not be <code>null</code> 1250 * @throws IOException if <code>source</code> URL cannot be opened 1251 * @throws IOException if <code>destination</code> is a directory 1252 * @throws IOException if <code>destination</code> cannot be written 1253 * @throws IOException if <code>destination</code> needs creating but can't be 1254 * @throws IOException if an IO error occurs during copying 1255 */ 1256 public static void copyURLToFile(URL source, File destination) throws IOException { 1257 InputStream input = source.openStream(); 1258 copyInputStreamToFile(input, destination); 1259 } 1260 1261 /** 1262 * Copies bytes from the URL <code>source</code> to a file 1263 * <code>destination</code>. The directories up to <code>destination</code> 1264 * will be created if they don't already exist. <code>destination</code> 1265 * will be overwritten if it already exists. 1266 * 1267 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code> 1268 * @param destination the non-directory <code>File</code> to write bytes to 1269 * (possibly overwriting), must not be <code>null</code> 1270 * @param connectionTimeout the number of milliseconds until this method 1271 * will timeout if no connection could be established to the <code>source</code> 1272 * @param readTimeout the number of milliseconds until this method will 1273 * timeout if no data could be read from the <code>source</code> 1274 * @throws IOException if <code>source</code> URL cannot be opened 1275 * @throws IOException if <code>destination</code> is a directory 1276 * @throws IOException if <code>destination</code> cannot be written 1277 * @throws IOException if <code>destination</code> needs creating but can't be 1278 * @throws IOException if an IO error occurs during copying 1279 * @since Commons IO 2.0 1280 */ 1281 public static void copyURLToFile(URL source, File destination, 1282 int connectionTimeout, int readTimeout) throws IOException { 1283 URLConnection connection = source.openConnection(); 1284 connection.setConnectTimeout(connectionTimeout); 1285 connection.setReadTimeout(readTimeout); 1286 InputStream input = connection.getInputStream(); 1287 copyInputStreamToFile(input, destination); 1288 } 1289 1290 /** 1291 * Copies bytes from an {@link InputStream} <code>source</code> to a file 1292 * <code>destination</code>. The directories up to <code>destination</code> 1293 * will be created if they don't already exist. <code>destination</code> 1294 * will be overwritten if it already exists. 1295 * 1296 * @param source the <code>InputStream</code> to copy bytes from, must not be <code>null</code> 1297 * @param destination the non-directory <code>File</code> to write bytes to 1298 * (possibly overwriting), must not be <code>null</code> 1299 * @throws IOException if <code>destination</code> is a directory 1300 * @throws IOException if <code>destination</code> cannot be written 1301 * @throws IOException if <code>destination</code> needs creating but can't be 1302 * @throws IOException if an IO error occurs during copying 1303 * @since Commons IO 2.0 1304 */ 1305 public static void copyInputStreamToFile(InputStream source, File destination) throws IOException { 1306 try { 1307 FileOutputStream output = openOutputStream(destination); 1308 try { 1309 IOUtils.copy(source, output); 1310 } finally { 1311 IOUtils.closeQuietly(output); 1312 } 1313 } finally { 1314 IOUtils.closeQuietly(source); 1315 } 1316 } 1317 1318 //----------------------------------------------------------------------- 1319 /** 1320 * Deletes a directory recursively. 1321 * 1322 * @param directory directory to delete 1323 * @throws IOException in case deletion is unsuccessful 1324 */ 1325 public static void deleteDirectory(File directory) throws IOException { 1326 if (!directory.exists()) { 1327 return; 1328 } 1329 1330 if (!isSymlink(directory)) { 1331 cleanDirectory(directory); 1332 } 1333 1334 if (!directory.delete()) { 1335 String message = 1336 "Unable to delete directory " + directory + "."; 1337 throw new IOException(message); 1338 } 1339 } 1340 1341 /** 1342 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. 1343 * <p> 1344 * The difference between File.delete() and this method are: 1345 * <ul> 1346 * <li>A directory to be deleted does not have to be empty.</li> 1347 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li> 1348 * </ul> 1349 * 1350 * @param file file or directory to delete, can be <code>null</code> 1351 * @return <code>true</code> if the file or directory was deleted, otherwise 1352 * <code>false</code> 1353 * 1354 * @since Commons IO 1.4 1355 */ 1356 public static boolean deleteQuietly(File file) { 1357 if (file == null) { 1358 return false; 1359 } 1360 try { 1361 if (file.isDirectory()) { 1362 cleanDirectory(file); 1363 } 1364 } catch (Exception ignored) { 1365 } 1366 1367 try { 1368 return file.delete(); 1369 } catch (Exception ignored) { 1370 return false; 1371 } 1372 } 1373 1374 /** 1375 * Cleans a directory without deleting it. 1376 * 1377 * @param directory directory to clean 1378 * @throws IOException in case cleaning is unsuccessful 1379 */ 1380 public static void cleanDirectory(File directory) throws IOException { 1381 if (!directory.exists()) { 1382 String message = directory + " does not exist"; 1383 throw new IllegalArgumentException(message); 1384 } 1385 1386 if (!directory.isDirectory()) { 1387 String message = directory + " is not a directory"; 1388 throw new IllegalArgumentException(message); 1389 } 1390 1391 File[] files = directory.listFiles(); 1392 if (files == null) { // null if security restricted 1393 throw new IOException("Failed to list contents of " + directory); 1394 } 1395 1396 IOException exception = null; 1397 for (File file : files) { 1398 try { 1399 forceDelete(file); 1400 } catch (IOException ioe) { 1401 exception = ioe; 1402 } 1403 } 1404 1405 if (null != exception) { 1406 throw exception; 1407 } 1408 } 1409 1410 //----------------------------------------------------------------------- 1411 /** 1412 * Waits for NFS to propagate a file creation, imposing a timeout. 1413 * <p> 1414 * This method repeatedly tests {@link File#exists()} until it returns 1415 * true up to the maximum time specified in seconds. 1416 * 1417 * @param file the file to check, must not be <code>null</code> 1418 * @param seconds the maximum time in seconds to wait 1419 * @return true if file exists 1420 * @throws NullPointerException if the file is <code>null</code> 1421 */ 1422 public static boolean waitFor(File file, int seconds) { 1423 int timeout = 0; 1424 int tick = 0; 1425 while (!file.exists()) { 1426 if (tick++ >= 10) { 1427 tick = 0; 1428 if (timeout++ > seconds) { 1429 return false; 1430 } 1431 } 1432 try { 1433 Thread.sleep(100); 1434 } catch (InterruptedException ignore) { 1435 // ignore exception 1436 } catch (Exception ex) { 1437 break; 1438 } 1439 } 1440 return true; 1441 } 1442 1443 //----------------------------------------------------------------------- 1444 /** 1445 * Reads the contents of a file into a String. 1446 * The file is always closed. 1447 * 1448 * @param file the file to read, must not be <code>null</code> 1449 * @param encoding the encoding to use, <code>null</code> means platform default 1450 * @return the file contents, never <code>null</code> 1451 * @throws IOException in case of an I/O error 1452 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1453 */ 1454 public static String readFileToString(File file, String encoding) throws IOException { 1455 InputStream in = null; 1456 try { 1457 in = openInputStream(file); 1458 return IOUtils.toString(in, encoding); 1459 } finally { 1460 IOUtils.closeQuietly(in); 1461 } 1462 } 1463 1464 1465 /** 1466 * Reads the contents of a file into a String using the default encoding for the VM. 1467 * The file is always closed. 1468 * 1469 * @param file the file to read, must not be <code>null</code> 1470 * @return the file contents, never <code>null</code> 1471 * @throws IOException in case of an I/O error 1472 * @since Commons IO 1.3.1 1473 */ 1474 public static String readFileToString(File file) throws IOException { 1475 return readFileToString(file, null); 1476 } 1477 1478 /** 1479 * Reads the contents of a file into a byte array. 1480 * The file is always closed. 1481 * 1482 * @param file the file to read, must not be <code>null</code> 1483 * @return the file contents, never <code>null</code> 1484 * @throws IOException in case of an I/O error 1485 * @since Commons IO 1.1 1486 */ 1487 public static byte[] readFileToByteArray(File file) throws IOException { 1488 InputStream in = null; 1489 try { 1490 in = openInputStream(file); 1491 return IOUtils.toByteArray(in, file.length()); 1492 } finally { 1493 IOUtils.closeQuietly(in); 1494 } 1495 } 1496 1497 /** 1498 * Reads the contents of a file line by line to a List of Strings. 1499 * The file is always closed. 1500 * 1501 * @param file the file to read, must not be <code>null</code> 1502 * @param encoding the encoding to use, <code>null</code> means platform default 1503 * @return the list of Strings representing each line in the file, never <code>null</code> 1504 * @throws IOException in case of an I/O error 1505 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1506 * @since Commons IO 1.1 1507 */ 1508 public static List<String> readLines(File file, String encoding) throws IOException { 1509 InputStream in = null; 1510 try { 1511 in = openInputStream(file); 1512 return IOUtils.readLines(in, encoding); 1513 } finally { 1514 IOUtils.closeQuietly(in); 1515 } 1516 } 1517 1518 /** 1519 * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. 1520 * The file is always closed. 1521 * 1522 * @param file the file to read, must not be <code>null</code> 1523 * @return the list of Strings representing each line in the file, never <code>null</code> 1524 * @throws IOException in case of an I/O error 1525 * @since Commons IO 1.3 1526 */ 1527 public static List<String> readLines(File file) throws IOException { 1528 return readLines(file, null); 1529 } 1530 1531 /** 1532 * Returns an Iterator for the lines in a <code>File</code>. 1533 * <p> 1534 * This method opens an <code>InputStream</code> for the file. 1535 * When you have finished with the iterator you should close the stream 1536 * to free internal resources. This can be done by calling the 1537 * {@link LineIterator#close()} or 1538 * {@link LineIterator#closeQuietly(LineIterator)} method. 1539 * <p> 1540 * The recommended usage pattern is: 1541 * <pre> 1542 * LineIterator it = FileUtils.lineIterator(file, "UTF-8"); 1543 * try { 1544 * while (it.hasNext()) { 1545 * String line = it.nextLine(); 1546 * /// do something with line 1547 * } 1548 * } finally { 1549 * LineIterator.closeQuietly(iterator); 1550 * } 1551 * </pre> 1552 * <p> 1553 * If an exception occurs during the creation of the iterator, the 1554 * underlying stream is closed. 1555 * 1556 * @param file the file to open for input, must not be <code>null</code> 1557 * @param encoding the encoding to use, <code>null</code> means platform default 1558 * @return an Iterator of the lines in the file, never <code>null</code> 1559 * @throws IOException in case of an I/O error (file closed) 1560 * @since Commons IO 1.2 1561 */ 1562 public static LineIterator lineIterator(File file, String encoding) throws IOException { 1563 InputStream in = null; 1564 try { 1565 in = openInputStream(file); 1566 return IOUtils.lineIterator(in, encoding); 1567 } catch (IOException ex) { 1568 IOUtils.closeQuietly(in); 1569 throw ex; 1570 } catch (RuntimeException ex) { 1571 IOUtils.closeQuietly(in); 1572 throw ex; 1573 } 1574 } 1575 1576 /** 1577 * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM. 1578 * 1579 * @param file the file to open for input, must not be <code>null</code> 1580 * @return an Iterator of the lines in the file, never <code>null</code> 1581 * @throws IOException in case of an I/O error (file closed) 1582 * @since Commons IO 1.3 1583 * @see #lineIterator(File, String) 1584 */ 1585 public static LineIterator lineIterator(File file) throws IOException { 1586 return lineIterator(file, null); 1587 } 1588 1589 //----------------------------------------------------------------------- 1590 /** 1591 * Writes a String to a file creating the file if it does not exist. 1592 * 1593 * NOTE: As from v1.3, the parent directories of the file will be created 1594 * if they do not exist. 1595 * 1596 * @param file the file to write 1597 * @param data the content to write to the file 1598 * @param encoding the encoding to use, <code>null</code> means platform default 1599 * @throws IOException in case of an I/O error 1600 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1601 */ 1602 public static void writeStringToFile(File file, String data, String encoding) throws IOException { 1603 writeStringToFile(file, data, encoding, false); 1604 } 1605 1606 /** 1607 * Writes a String to a file creating the file if it does not exist. 1608 * 1609 * @param file the file to write 1610 * @param data the content to write to the file 1611 * @param encoding the encoding to use, <code>null</code> means platform default 1612 * @param append if <code>true</code>, then the String will be added to the 1613 * end of the file rather than overwriting 1614 * @throws IOException in case of an I/O error 1615 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1616 * @since Commons IO 2.1 1617 */ 1618 public static void writeStringToFile(File file, String data, String encoding, boolean append) throws IOException { 1619 OutputStream out = null; 1620 try { 1621 out = openOutputStream(file, append); 1622 IOUtils.write(data, out, encoding); 1623 } finally { 1624 IOUtils.closeQuietly(out); 1625 } 1626 } 1627 1628 /** 1629 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1630 * 1631 * @param file the file to write 1632 * @param data the content to write to the file 1633 * @throws IOException in case of an I/O error 1634 */ 1635 public static void writeStringToFile(File file, String data) throws IOException { 1636 writeStringToFile(file, data, null, false); 1637 } 1638 1639 /** 1640 * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. 1641 * 1642 * @param file the file to write 1643 * @param data the content to write to the file 1644 * @param append if <code>true</code>, then the String will be added to the 1645 * end of the file rather than overwriting 1646 * @throws IOException in case of an I/O error 1647 * @since Commons IO 2.1 1648 */ 1649 public static void writeStringToFile(File file, String data, boolean append) throws IOException { 1650 writeStringToFile(file, data, null, append); 1651 } 1652 1653 /** 1654 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. 1655 * 1656 * @param file the file to write 1657 * @param data the content to write to the file 1658 * @throws IOException in case of an I/O error 1659 * @since Commons IO 2.0 1660 */ 1661 public static void write(File file, CharSequence data) throws IOException { 1662 write(file, data, null, false); 1663 } 1664 1665 /** 1666 * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. 1667 * 1668 * @param file the file to write 1669 * @param data the content to write to the file 1670 * @param append if <code>true</code>, then the data will be added to the 1671 * end of the file rather than overwriting 1672 * @throws IOException in case of an I/O error 1673 * @since Commons IO 2.1 1674 */ 1675 public static void write(File file, CharSequence data, boolean append) throws IOException { 1676 write(file, data, null, append); 1677 } 1678 1679 /** 1680 * Writes a CharSequence to a file creating the file if it does not exist. 1681 * 1682 * @param file the file to write 1683 * @param data the content to write to the file 1684 * @param encoding the encoding to use, <code>null</code> means platform default 1685 * @throws IOException in case of an I/O error 1686 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1687 * @since Commons IO 2.0 1688 */ 1689 public static void write(File file, CharSequence data, String encoding) throws IOException { 1690 write(file, data, encoding, false); 1691 } 1692 1693 /** 1694 * Writes a CharSequence to a file creating the file if it does not exist. 1695 * 1696 * @param file the file to write 1697 * @param data the content to write to the file 1698 * @param encoding the encoding to use, <code>null</code> means platform default 1699 * @param append if <code>true</code>, then the data will be added to the 1700 * end of the file rather than overwriting 1701 * @throws IOException in case of an I/O error 1702 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1703 * @since IO 2.1 1704 */ 1705 public static void write(File file, CharSequence data, String encoding, boolean append) throws IOException { 1706 String str = data == null ? null : data.toString(); 1707 writeStringToFile(file, str, encoding, append); 1708 } 1709 1710 /** 1711 * Writes a byte array to a file creating the file if it does not exist. 1712 * <p> 1713 * NOTE: As from v1.3, the parent directories of the file will be created 1714 * if they do not exist. 1715 * 1716 * @param file the file to write to 1717 * @param data the content to write to the file 1718 * @throws IOException in case of an I/O error 1719 * @since Commons IO 1.1 1720 */ 1721 public static void writeByteArrayToFile(File file, byte[] data) throws IOException { 1722 writeByteArrayToFile(file, data, false); 1723 } 1724 1725 /** 1726 * Writes a byte array to a file creating the file if it does not exist. 1727 * 1728 * @param file the file to write to 1729 * @param data the content to write to the file 1730 * @param append if <code>true</code>, then bytes will be added to the 1731 * end of the file rather than overwriting 1732 * @throws IOException in case of an I/O error 1733 * @since IO 2.1 1734 */ 1735 public static void writeByteArrayToFile(File file, byte[] data, boolean append) throws IOException { 1736 OutputStream out = null; 1737 try { 1738 out = openOutputStream(file, append); 1739 out.write(data); 1740 } finally { 1741 IOUtils.closeQuietly(out); 1742 } 1743 } 1744 1745 /** 1746 * Writes the <code>toString()</code> value of each item in a collection to 1747 * the specified <code>File</code> line by line. 1748 * The specified character encoding and the default line ending will be used. 1749 * <p> 1750 * NOTE: As from v1.3, the parent directories of the file will be created 1751 * if they do not exist. 1752 * 1753 * @param file the file to write to 1754 * @param encoding the encoding to use, <code>null</code> means platform default 1755 * @param lines the lines to write, <code>null</code> entries produce blank lines 1756 * @throws IOException in case of an I/O error 1757 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1758 * @since Commons IO 1.1 1759 */ 1760 public static void writeLines(File file, String encoding, Collection<?> lines) throws IOException { 1761 writeLines(file, encoding, lines, null, false); 1762 } 1763 1764 /** 1765 * Writes the <code>toString()</code> value of each item in a collection to 1766 * the specified <code>File</code> line by line, optionally appending. 1767 * The specified character encoding and the default line ending will be used. 1768 * 1769 * @param file the file to write to 1770 * @param encoding the encoding to use, <code>null</code> means platform default 1771 * @param lines the lines to write, <code>null</code> entries produce blank lines 1772 * @param append if <code>true</code>, then the lines will be added to the 1773 * end of the file rather than overwriting 1774 * @throws IOException in case of an I/O error 1775 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1776 * @since Commons IO 2.1 1777 */ 1778 public static void writeLines(File file, String encoding, Collection<?> lines, boolean append) throws IOException { 1779 writeLines(file, encoding, lines, null, append); 1780 } 1781 1782 /** 1783 * Writes the <code>toString()</code> value of each item in a collection to 1784 * the specified <code>File</code> line by line. 1785 * The default VM encoding and the default line ending will be used. 1786 * 1787 * @param file the file to write to 1788 * @param lines the lines to write, <code>null</code> entries produce blank lines 1789 * @throws IOException in case of an I/O error 1790 * @since Commons IO 1.3 1791 */ 1792 public static void writeLines(File file, Collection<?> lines) throws IOException { 1793 writeLines(file, null, lines, null, false); 1794 } 1795 1796 /** 1797 * Writes the <code>toString()</code> value of each item in a collection to 1798 * the specified <code>File</code> line by line. 1799 * The default VM encoding and the default line ending will be used. 1800 * 1801 * @param file the file to write to 1802 * @param lines the lines to write, <code>null</code> entries produce blank lines 1803 * @param append if <code>true</code>, then the lines will be added to the 1804 * end of the file rather than overwriting 1805 * @throws IOException in case of an I/O error 1806 * @since Commons IO 2.1 1807 */ 1808 public static void writeLines(File file, Collection<?> lines, boolean append) throws IOException { 1809 writeLines(file, null, lines, null, append); 1810 } 1811 1812 /** 1813 * Writes the <code>toString()</code> value of each item in a collection to 1814 * the specified <code>File</code> line by line. 1815 * The specified character encoding and the line ending will be used. 1816 * <p> 1817 * NOTE: As from v1.3, the parent directories of the file will be created 1818 * if they do not exist. 1819 * 1820 * @param file the file to write to 1821 * @param encoding the encoding to use, <code>null</code> means platform default 1822 * @param lines the lines to write, <code>null</code> entries produce blank lines 1823 * @param lineEnding the line separator to use, <code>null</code> is system default 1824 * @throws IOException in case of an I/O error 1825 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1826 * @since Commons IO 1.1 1827 */ 1828 public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding) 1829 throws IOException { 1830 writeLines(file, encoding, lines, lineEnding, false); 1831 } 1832 1833 /** 1834 * Writes the <code>toString()</code> value of each item in a collection to 1835 * the specified <code>File</code> line by line. 1836 * The specified character encoding and the line ending will be used. 1837 * 1838 * @param file the file to write to 1839 * @param encoding the encoding to use, <code>null</code> means platform default 1840 * @param lines the lines to write, <code>null</code> entries produce blank lines 1841 * @param lineEnding the line separator to use, <code>null</code> is system default 1842 * @param append if <code>true</code>, then the lines will be added to the 1843 * end of the file rather than overwriting 1844 * @throws IOException in case of an I/O error 1845 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM 1846 * @since Commons IO 2.1 1847 */ 1848 public static void writeLines(File file, String encoding, Collection<?> lines, String lineEnding, boolean append) 1849 throws IOException { 1850 OutputStream out = null; 1851 try { 1852 out = openOutputStream(file, append); 1853 IOUtils.writeLines(lines, lineEnding, out, encoding); 1854 } finally { 1855 IOUtils.closeQuietly(out); 1856 } 1857 } 1858 1859 /** 1860 * Writes the <code>toString()</code> value of each item in a collection to 1861 * the specified <code>File</code> line by line. 1862 * The default VM encoding and the specified line ending will be used. 1863 * 1864 * @param file the file to write to 1865 * @param lines the lines to write, <code>null</code> entries produce blank lines 1866 * @param lineEnding the line separator to use, <code>null</code> is system default 1867 * @throws IOException in case of an I/O error 1868 * @since Commons IO 1.3 1869 */ 1870 public static void writeLines(File file, Collection<?> lines, String lineEnding) throws IOException { 1871 writeLines(file, null, lines, lineEnding, false); 1872 } 1873 1874 /** 1875 * Writes the <code>toString()</code> value of each item in a collection to 1876 * the specified <code>File</code> line by line. 1877 * The default VM encoding and the specified line ending will be used. 1878 * 1879 * @param file the file to write to 1880 * @param lines the lines to write, <code>null</code> entries produce blank lines 1881 * @param lineEnding the line separator to use, <code>null</code> is system default 1882 * @param append if <code>true</code>, then the lines will be added to the 1883 * end of the file rather than overwriting 1884 * @throws IOException in case of an I/O error 1885 * @since Commons IO 2.1 1886 */ 1887 public static void writeLines(File file, Collection<?> lines, String lineEnding, boolean append) 1888 throws IOException { 1889 writeLines(file, null, lines, lineEnding, append); 1890 } 1891 1892 //----------------------------------------------------------------------- 1893 /** 1894 * Deletes a file. If file is a directory, delete it and all sub-directories. 1895 * <p> 1896 * The difference between File.delete() and this method are: 1897 * <ul> 1898 * <li>A directory to be deleted does not have to be empty.</li> 1899 * <li>You get exceptions when a file or directory cannot be deleted. 1900 * (java.io.File methods returns a boolean)</li> 1901 * </ul> 1902 * 1903 * @param file file or directory to delete, must not be <code>null</code> 1904 * @throws NullPointerException if the directory is <code>null</code> 1905 * @throws FileNotFoundException if the file was not found 1906 * @throws IOException in case deletion is unsuccessful 1907 */ 1908 public static void forceDelete(File file) throws IOException { 1909 if (file.isDirectory()) { 1910 deleteDirectory(file); 1911 } else { 1912 boolean filePresent = file.exists(); 1913 if (!file.delete()) { 1914 if (!filePresent){ 1915 throw new FileNotFoundException("File does not exist: " + file); 1916 } 1917 String message = 1918 "Unable to delete file: " + file; 1919 throw new IOException(message); 1920 } 1921 } 1922 } 1923 1924 /** 1925 * Schedules a file to be deleted when JVM exits. 1926 * If file is directory delete it and all sub-directories. 1927 * 1928 * @param file file or directory to delete, must not be <code>null</code> 1929 * @throws NullPointerException if the file is <code>null</code> 1930 * @throws IOException in case deletion is unsuccessful 1931 */ 1932 public static void forceDeleteOnExit(File file) throws IOException { 1933 if (file.isDirectory()) { 1934 deleteDirectoryOnExit(file); 1935 } else { 1936 file.deleteOnExit(); 1937 } 1938 } 1939 1940 /** 1941 * Schedules a directory recursively for deletion on JVM exit. 1942 * 1943 * @param directory directory to delete, must not be <code>null</code> 1944 * @throws NullPointerException if the directory is <code>null</code> 1945 * @throws IOException in case deletion is unsuccessful 1946 */ 1947 private static void deleteDirectoryOnExit(File directory) throws IOException { 1948 if (!directory.exists()) { 1949 return; 1950 } 1951 1952 if (!isSymlink(directory)) { 1953 cleanDirectoryOnExit(directory); 1954 } 1955 directory.deleteOnExit(); 1956 } 1957 1958 /** 1959 * Cleans a directory without deleting it. 1960 * 1961 * @param directory directory to clean, must not be <code>null</code> 1962 * @throws NullPointerException if the directory is <code>null</code> 1963 * @throws IOException in case cleaning is unsuccessful 1964 */ 1965 private static void cleanDirectoryOnExit(File directory) throws IOException { 1966 if (!directory.exists()) { 1967 String message = directory + " does not exist"; 1968 throw new IllegalArgumentException(message); 1969 } 1970 1971 if (!directory.isDirectory()) { 1972 String message = directory + " is not a directory"; 1973 throw new IllegalArgumentException(message); 1974 } 1975 1976 File[] files = directory.listFiles(); 1977 if (files == null) { // null if security restricted 1978 throw new IOException("Failed to list contents of " + directory); 1979 } 1980 1981 IOException exception = null; 1982 for (File file : files) { 1983 try { 1984 forceDeleteOnExit(file); 1985 } catch (IOException ioe) { 1986 exception = ioe; 1987 } 1988 } 1989 1990 if (null != exception) { 1991 throw exception; 1992 } 1993 } 1994 1995 /** 1996 * Makes a directory, including any necessary but nonexistent parent 1997 * directories. If a file already exists with specified name but it is 1998 * not a directory then an IOException is thrown. 1999 * If the directory cannot be created (or does not already exist) 2000 * then an IOException is thrown. 2001 * 2002 * @param directory directory to create, must not be <code>null</code> 2003 * @throws NullPointerException if the directory is <code>null</code> 2004 * @throws IOException if the directory cannot be created or the file already exists but is not a directory 2005 */ 2006 public static void forceMkdir(File directory) throws IOException { 2007 if (directory.exists()) { 2008 if (!directory.isDirectory()) { 2009 String message = 2010 "File " 2011 + directory 2012 + " exists and is " 2013 + "not a directory. Unable to create directory."; 2014 throw new IOException(message); 2015 } 2016 } else { 2017 if (!directory.mkdirs()) { 2018 // Double-check that some other thread or process hasn't made 2019 // the directory in the background 2020 if (!directory.isDirectory()) 2021 { 2022 String message = 2023 "Unable to create directory " + directory; 2024 throw new IOException(message); 2025 } 2026 } 2027 } 2028 } 2029 2030 //----------------------------------------------------------------------- 2031 /** 2032 * Returns the size of the specified file or directory. If the provided 2033 * {@link File} is a regular file, then the file's length is returned. 2034 * If the argument is a directory, then the size of the directory is 2035 * calculated recursively. If a directory or subdirectory is security 2036 * restricted, its size will not be included. 2037 * 2038 * @param file the regular file or directory to return the size 2039 * of (must not be <code>null</code>). 2040 * 2041 * @return the length of the file, or recursive size of the directory, 2042 * provided (in bytes). 2043 * 2044 * @throws NullPointerException if the file is <code>null</code> 2045 * @throws IllegalArgumentException if the file does not exist. 2046 * 2047 * @since Commons IO 2.0 2048 */ 2049 public static long sizeOf(File file) { 2050 2051 if (!file.exists()) { 2052 String message = file + " does not exist"; 2053 throw new IllegalArgumentException(message); 2054 } 2055 2056 if (file.isDirectory()) { 2057 return sizeOfDirectory(file); 2058 } else { 2059 return file.length(); 2060 } 2061 2062 } 2063 2064 /** 2065 * Counts the size of a directory recursively (sum of the length of all files). 2066 * 2067 * @param directory directory to inspect, must not be <code>null</code> 2068 * @return size of directory in bytes, 0 if directory is security restricted 2069 * @throws NullPointerException if the directory is <code>null</code> 2070 */ 2071 public static long sizeOfDirectory(File directory) { 2072 if (!directory.exists()) { 2073 String message = directory + " does not exist"; 2074 throw new IllegalArgumentException(message); 2075 } 2076 2077 if (!directory.isDirectory()) { 2078 String message = directory + " is not a directory"; 2079 throw new IllegalArgumentException(message); 2080 } 2081 2082 long size = 0; 2083 2084 File[] files = directory.listFiles(); 2085 if (files == null) { // null if security restricted 2086 return 0L; 2087 } 2088 for (File file : files) { 2089 size += sizeOf(file); 2090 } 2091 2092 return size; 2093 } 2094 2095 //----------------------------------------------------------------------- 2096 /** 2097 * Tests if the specified <code>File</code> is newer than the reference 2098 * <code>File</code>. 2099 * 2100 * @param file the <code>File</code> of which the modification date must 2101 * be compared, must not be <code>null</code> 2102 * @param reference the <code>File</code> of which the modification date 2103 * is used, must not be <code>null</code> 2104 * @return true if the <code>File</code> exists and has been modified more 2105 * recently than the reference <code>File</code> 2106 * @throws IllegalArgumentException if the file is <code>null</code> 2107 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist 2108 */ 2109 public static boolean isFileNewer(File file, File reference) { 2110 if (reference == null) { 2111 throw new IllegalArgumentException("No specified reference file"); 2112 } 2113 if (!reference.exists()) { 2114 throw new IllegalArgumentException("The reference file '" 2115 + reference + "' doesn't exist"); 2116 } 2117 return isFileNewer(file, reference.lastModified()); 2118 } 2119 2120 /** 2121 * Tests if the specified <code>File</code> is newer than the specified 2122 * <code>Date</code>. 2123 * 2124 * @param file the <code>File</code> of which the modification date 2125 * must be compared, must not be <code>null</code> 2126 * @param date the date reference, must not be <code>null</code> 2127 * @return true if the <code>File</code> exists and has been modified 2128 * after the given <code>Date</code>. 2129 * @throws IllegalArgumentException if the file is <code>null</code> 2130 * @throws IllegalArgumentException if the date is <code>null</code> 2131 */ 2132 public static boolean isFileNewer(File file, Date date) { 2133 if (date == null) { 2134 throw new IllegalArgumentException("No specified date"); 2135 } 2136 return isFileNewer(file, date.getTime()); 2137 } 2138 2139 /** 2140 * Tests if the specified <code>File</code> is newer than the specified 2141 * time reference. 2142 * 2143 * @param file the <code>File</code> of which the modification date must 2144 * be compared, must not be <code>null</code> 2145 * @param timeMillis the time reference measured in milliseconds since the 2146 * epoch (00:00:00 GMT, January 1, 1970) 2147 * @return true if the <code>File</code> exists and has been modified after 2148 * the given time reference. 2149 * @throws IllegalArgumentException if the file is <code>null</code> 2150 */ 2151 public static boolean isFileNewer(File file, long timeMillis) { 2152 if (file == null) { 2153 throw new IllegalArgumentException("No specified file"); 2154 } 2155 if (!file.exists()) { 2156 return false; 2157 } 2158 return file.lastModified() > timeMillis; 2159 } 2160 2161 2162 //----------------------------------------------------------------------- 2163 /** 2164 * Tests if the specified <code>File</code> is older than the reference 2165 * <code>File</code>. 2166 * 2167 * @param file the <code>File</code> of which the modification date must 2168 * be compared, must not be <code>null</code> 2169 * @param reference the <code>File</code> of which the modification date 2170 * is used, must not be <code>null</code> 2171 * @return true if the <code>File</code> exists and has been modified before 2172 * the reference <code>File</code> 2173 * @throws IllegalArgumentException if the file is <code>null</code> 2174 * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist 2175 */ 2176 public static boolean isFileOlder(File file, File reference) { 2177 if (reference == null) { 2178 throw new IllegalArgumentException("No specified reference file"); 2179 } 2180 if (!reference.exists()) { 2181 throw new IllegalArgumentException("The reference file '" 2182 + reference + "' doesn't exist"); 2183 } 2184 return isFileOlder(file, reference.lastModified()); 2185 } 2186 2187 /** 2188 * Tests if the specified <code>File</code> is older than the specified 2189 * <code>Date</code>. 2190 * 2191 * @param file the <code>File</code> of which the modification date 2192 * must be compared, must not be <code>null</code> 2193 * @param date the date reference, must not be <code>null</code> 2194 * @return true if the <code>File</code> exists and has been modified 2195 * before the given <code>Date</code>. 2196 * @throws IllegalArgumentException if the file is <code>null</code> 2197 * @throws IllegalArgumentException if the date is <code>null</code> 2198 */ 2199 public static boolean isFileOlder(File file, Date date) { 2200 if (date == null) { 2201 throw new IllegalArgumentException("No specified date"); 2202 } 2203 return isFileOlder(file, date.getTime()); 2204 } 2205 2206 /** 2207 * Tests if the specified <code>File</code> is older than the specified 2208 * time reference. 2209 * 2210 * @param file the <code>File</code> of which the modification date must 2211 * be compared, must not be <code>null</code> 2212 * @param timeMillis the time reference measured in milliseconds since the 2213 * epoch (00:00:00 GMT, January 1, 1970) 2214 * @return true if the <code>File</code> exists and has been modified before 2215 * the given time reference. 2216 * @throws IllegalArgumentException if the file is <code>null</code> 2217 */ 2218 public static boolean isFileOlder(File file, long timeMillis) { 2219 if (file == null) { 2220 throw new IllegalArgumentException("No specified file"); 2221 } 2222 if (!file.exists()) { 2223 return false; 2224 } 2225 return file.lastModified() < timeMillis; 2226 } 2227 2228 //----------------------------------------------------------------------- 2229 /** 2230 * Computes the checksum of a file using the CRC32 checksum routine. 2231 * The value of the checksum is returned. 2232 * 2233 * @param file the file to checksum, must not be <code>null</code> 2234 * @return the checksum value 2235 * @throws NullPointerException if the file or checksum is <code>null</code> 2236 * @throws IllegalArgumentException if the file is a directory 2237 * @throws IOException if an IO error occurs reading the file 2238 * @since Commons IO 1.3 2239 */ 2240 public static long checksumCRC32(File file) throws IOException { 2241 CRC32 crc = new CRC32(); 2242 checksum(file, crc); 2243 return crc.getValue(); 2244 } 2245 2246 /** 2247 * Computes the checksum of a file using the specified checksum object. 2248 * Multiple files may be checked using one <code>Checksum</code> instance 2249 * if desired simply by reusing the same checksum object. 2250 * For example: 2251 * <pre> 2252 * long csum = FileUtils.checksum(file, new CRC32()).getValue(); 2253 * </pre> 2254 * 2255 * @param file the file to checksum, must not be <code>null</code> 2256 * @param checksum the checksum object to be used, must not be <code>null</code> 2257 * @return the checksum specified, updated with the content of the file 2258 * @throws NullPointerException if the file or checksum is <code>null</code> 2259 * @throws IllegalArgumentException if the file is a directory 2260 * @throws IOException if an IO error occurs reading the file 2261 * @since Commons IO 1.3 2262 */ 2263 public static Checksum checksum(File file, Checksum checksum) throws IOException { 2264 if (file.isDirectory()) { 2265 throw new IllegalArgumentException("Checksums can't be computed on directories"); 2266 } 2267 InputStream in = null; 2268 try { 2269 in = new CheckedInputStream(new FileInputStream(file), checksum); 2270 IOUtils.copy(in, new NullOutputStream()); 2271 } finally { 2272 IOUtils.closeQuietly(in); 2273 } 2274 return checksum; 2275 } 2276 2277 /** 2278 * Moves a directory. 2279 * <p> 2280 * When the destination directory is on another file system, do a "copy and delete". 2281 * 2282 * @param srcDir the directory to be moved 2283 * @param destDir the destination directory 2284 * @throws NullPointerException if source or destination is <code>null</code> 2285 * @throws FileExistsException if the destination directory exists 2286 * @throws IOException if source or destination is invalid 2287 * @throws IOException if an IO error occurs moving the file 2288 * @since Commons IO 1.4 2289 */ 2290 public static void moveDirectory(File srcDir, File destDir) throws IOException { 2291 if (srcDir == null) { 2292 throw new NullPointerException("Source must not be null"); 2293 } 2294 if (destDir == null) { 2295 throw new NullPointerException("Destination must not be null"); 2296 } 2297 if (!srcDir.exists()) { 2298 throw new FileNotFoundException("Source '" + srcDir + "' does not exist"); 2299 } 2300 if (!srcDir.isDirectory()) { 2301 throw new IOException("Source '" + srcDir + "' is not a directory"); 2302 } 2303 if (destDir.exists()) { 2304 throw new FileExistsException("Destination '" + destDir + "' already exists"); 2305 } 2306 boolean rename = srcDir.renameTo(destDir); 2307 if (!rename) { 2308 copyDirectory( srcDir, destDir ); 2309 deleteDirectory( srcDir ); 2310 if (srcDir.exists()) { 2311 throw new IOException("Failed to delete original directory '" + srcDir + 2312 "' after copy to '" + destDir + "'"); 2313 } 2314 } 2315 } 2316 2317 /** 2318 * Moves a directory to another directory. 2319 * 2320 * @param src the file to be moved 2321 * @param destDir the destination file 2322 * @param createDestDir If <code>true</code> create the destination directory, 2323 * otherwise if <code>false</code> throw an IOException 2324 * @throws NullPointerException if source or destination is <code>null</code> 2325 * @throws FileExistsException if the directory exists in the destination directory 2326 * @throws IOException if source or destination is invalid 2327 * @throws IOException if an IO error occurs moving the file 2328 * @since Commons IO 1.4 2329 */ 2330 public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException { 2331 if (src == null) { 2332 throw new NullPointerException("Source must not be null"); 2333 } 2334 if (destDir == null) { 2335 throw new NullPointerException("Destination directory must not be null"); 2336 } 2337 if (!destDir.exists() && createDestDir) { 2338 destDir.mkdirs(); 2339 } 2340 if (!destDir.exists()) { 2341 throw new FileNotFoundException("Destination directory '" + destDir + 2342 "' does not exist [createDestDir=" + createDestDir +"]"); 2343 } 2344 if (!destDir.isDirectory()) { 2345 throw new IOException("Destination '" + destDir + "' is not a directory"); 2346 } 2347 moveDirectory(src, new File(destDir, src.getName())); 2348 2349 } 2350 2351 /** 2352 * Moves a file. 2353 * <p> 2354 * When the destination file is on another file system, do a "copy and delete". 2355 * 2356 * @param srcFile the file to be moved 2357 * @param destFile the destination file 2358 * @throws NullPointerException if source or destination is <code>null</code> 2359 * @throws FileExistsException if the destination file exists 2360 * @throws IOException if source or destination is invalid 2361 * @throws IOException if an IO error occurs moving the file 2362 * @since Commons IO 1.4 2363 */ 2364 public static void moveFile(File srcFile, File destFile) throws IOException { 2365 if (srcFile == null) { 2366 throw new NullPointerException("Source must not be null"); 2367 } 2368 if (destFile == null) { 2369 throw new NullPointerException("Destination must not be null"); 2370 } 2371 if (!srcFile.exists()) { 2372 throw new FileNotFoundException("Source '" + srcFile + "' does not exist"); 2373 } 2374 if (srcFile.isDirectory()) { 2375 throw new IOException("Source '" + srcFile + "' is a directory"); 2376 } 2377 if (destFile.exists()) { 2378 throw new FileExistsException("Destination '" + destFile + "' already exists"); 2379 } 2380 if (destFile.isDirectory()) { 2381 throw new IOException("Destination '" + destFile + "' is a directory"); 2382 } 2383 boolean rename = srcFile.renameTo(destFile); 2384 if (!rename) { 2385 copyFile( srcFile, destFile ); 2386 if (!srcFile.delete()) { 2387 FileUtils.deleteQuietly(destFile); 2388 throw new IOException("Failed to delete original file '" + srcFile + 2389 "' after copy to '" + destFile + "'"); 2390 } 2391 } 2392 } 2393 2394 /** 2395 * Moves a file to a directory. 2396 * 2397 * @param srcFile the file to be moved 2398 * @param destDir the destination file 2399 * @param createDestDir If <code>true</code> create the destination directory, 2400 * otherwise if <code>false</code> throw an IOException 2401 * @throws NullPointerException if source or destination is <code>null</code> 2402 * @throws FileExistsException if the destination file exists 2403 * @throws IOException if source or destination is invalid 2404 * @throws IOException if an IO error occurs moving the file 2405 * @since Commons IO 1.4 2406 */ 2407 public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException { 2408 if (srcFile == null) { 2409 throw new NullPointerException("Source must not be null"); 2410 } 2411 if (destDir == null) { 2412 throw new NullPointerException("Destination directory must not be null"); 2413 } 2414 if (!destDir.exists() && createDestDir) { 2415 destDir.mkdirs(); 2416 } 2417 if (!destDir.exists()) { 2418 throw new FileNotFoundException("Destination directory '" + destDir + 2419 "' does not exist [createDestDir=" + createDestDir +"]"); 2420 } 2421 if (!destDir.isDirectory()) { 2422 throw new IOException("Destination '" + destDir + "' is not a directory"); 2423 } 2424 moveFile(srcFile, new File(destDir, srcFile.getName())); 2425 } 2426 2427 /** 2428 * Moves a file or directory to the destination directory. 2429 * <p> 2430 * When the destination is on another file system, do a "copy and delete". 2431 * 2432 * @param src the file or directory to be moved 2433 * @param destDir the destination directory 2434 * @param createDestDir If <code>true</code> create the destination directory, 2435 * otherwise if <code>false</code> throw an IOException 2436 * @throws NullPointerException if source or destination is <code>null</code> 2437 * @throws FileExistsException if the directory or file exists in the destination directory 2438 * @throws IOException if source or destination is invalid 2439 * @throws IOException if an IO error occurs moving the file 2440 * @since Commons IO 1.4 2441 */ 2442 public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException { 2443 if (src == null) { 2444 throw new NullPointerException("Source must not be null"); 2445 } 2446 if (destDir == null) { 2447 throw new NullPointerException("Destination must not be null"); 2448 } 2449 if (!src.exists()) { 2450 throw new FileNotFoundException("Source '" + src + "' does not exist"); 2451 } 2452 if (src.isDirectory()) { 2453 moveDirectoryToDirectory(src, destDir, createDestDir); 2454 } else { 2455 moveFileToDirectory(src, destDir, createDestDir); 2456 } 2457 } 2458 2459 /** 2460 * Determines whether the specified file is a Symbolic Link rather than an actual file. 2461 * <p> 2462 * Will not return true if there is a Symbolic Link anywhere in the path, 2463 * only if the specific file is. 2464 * 2465 * @param file the file to check 2466 * @return true if the file is a Symbolic Link 2467 * @throws IOException if an IO error occurs while checking the file 2468 * @since Commons IO 2.0 2469 */ 2470 public static boolean isSymlink(File file) throws IOException { 2471 if (file == null) { 2472 throw new NullPointerException("File must not be null"); 2473 } 2474 if (FilenameUtils.isSystemWindows()) { 2475 return false; 2476 } 2477 File fileInCanonicalDir = null; 2478 if (file.getParent() == null) { 2479 fileInCanonicalDir = file; 2480 } else { 2481 File canonicalDir = file.getParentFile().getCanonicalFile(); 2482 fileInCanonicalDir = new File(canonicalDir, file.getName()); 2483 } 2484 2485 if (fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile())) { 2486 return false; 2487 } else { 2488 return true; 2489 } 2490 } 2491 }