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 }