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