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