1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.io;
18
19 import java.io.File;
20 import java.io.FileFilter;
21 import java.io.FileInputStream;
22 import java.io.FileNotFoundException;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.net.URL;
28 import java.nio.channels.FileChannel;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Date;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.zip.CRC32;
35 import java.util.zip.CheckedInputStream;
36 import java.util.zip.Checksum;
37
38 import org.apache.commons.io.filefilter.DirectoryFileFilter;
39 import org.apache.commons.io.filefilter.FalseFileFilter;
40 import org.apache.commons.io.filefilter.FileFilterUtils;
41 import org.apache.commons.io.filefilter.IOFileFilter;
42 import org.apache.commons.io.filefilter.SuffixFileFilter;
43 import org.apache.commons.io.filefilter.TrueFileFilter;
44 import org.apache.commons.io.output.NullOutputStream;
45
46 /**
47 * General file manipulation utilities.
48 * <p>
49 * Facilities are provided in the following areas:
50 * <ul>
51 * <li>writing to a file
52 * <li>reading from a file
53 * <li>make a directory including parent directories
54 * <li>copying files and directories
55 * <li>deleting files and directories
56 * <li>converting to and from a URL
57 * <li>listing files and directories by filter and extension
58 * <li>comparing file content
59 * <li>file last changed date
60 * <li>calculating a checksum
61 * </ul>
62 * <p>
63 * Origin of code: Excalibur, Alexandria, Commons-Utils
64 *
65 * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
66 * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
67 * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
68 * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
69 * @author <a href="mailto:peter@apache.org">Peter Donald</a>
70 * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
71 * @author Matthew Hawthorne
72 * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
73 * @author Stephen Colebourne
74 * @author Ian Springer
75 * @author Chris Eldredge
76 * @author Jim Harrington
77 * @author Niall Pemberton
78 * @author Sandy McArthur
79 * @version $Id: FileUtils.java 619188 2008-02-06 22:33:04Z niallp $
80 */
81 public class FileUtils {
82
83 /**
84 * Instances should NOT be constructed in standard programming.
85 */
86 public FileUtils() {
87 super();
88 }
89
90 /**
91 * The number of bytes in a kilobyte.
92 */
93 public static final long ONE_KB = 1024;
94
95 /**
96 * The number of bytes in a megabyte.
97 */
98 public static final long ONE_MB = ONE_KB * ONE_KB;
99
100 /**
101 * The number of bytes in a gigabyte.
102 */
103 public static final long ONE_GB = ONE_KB * ONE_MB;
104
105 /**
106 * An empty array of type <code>File</code>.
107 */
108 public static final File[] EMPTY_FILE_ARRAY = new File[0];
109
110 //-----------------------------------------------------------------------
111 /**
112 * Opens a {@link FileInputStream} for the specified file, providing better
113 * error messages than simply calling <code>new FileInputStream(file)</code>.
114 * <p>
115 * At the end of the method either the stream will be successfully opened,
116 * or an exception will have been thrown.
117 * <p>
118 * An exception is thrown if the file does not exist.
119 * An exception is thrown if the file object exists but is a directory.
120 * An exception is thrown if the file exists but cannot be read.
121 *
122 * @param file the file to open for input, must not be <code>null</code>
123 * @return a new {@link FileInputStream} for the specified file
124 * @throws FileNotFoundException if the file does not exist
125 * @throws IOException if the file object is a directory
126 * @throws IOException if the file cannot be read
127 * @since Commons IO 1.3
128 */
129 public static FileInputStream openInputStream(File file) throws IOException {
130 if (file.exists()) {
131 if (file.isDirectory()) {
132 throw new IOException("File '" + file + "' exists but is a directory");
133 }
134 if (file.canRead() == false) {
135 throw new IOException("File '" + file + "' cannot be read");
136 }
137 } else {
138 throw new FileNotFoundException("File '" + file + "' does not exist");
139 }
140 return new FileInputStream(file);
141 }
142
143 //-----------------------------------------------------------------------
144 /**
145 * Opens a {@link FileOutputStream} for the specified file, checking and
146 * creating the parent directory if it does not exist.
147 * <p>
148 * At the end of the method either the stream will be successfully opened,
149 * or an exception will have been thrown.
150 * <p>
151 * The parent directory will be created if it does not exist.
152 * The file will be created if it does not exist.
153 * An exception is thrown if the file object exists but is a directory.
154 * An exception is thrown if the file exists but cannot be written to.
155 * An exception is thrown if the parent directory cannot be created.
156 *
157 * @param file the file to open for output, must not be <code>null</code>
158 * @return a new {@link FileOutputStream} for the specified file
159 * @throws IOException if the file object is a directory
160 * @throws IOException if the file cannot be written to
161 * @throws IOException if a parent directory needs creating but that fails
162 * @since Commons IO 1.3
163 */
164 public static FileOutputStream openOutputStream(File file) throws IOException {
165 if (file.exists()) {
166 if (file.isDirectory()) {
167 throw new IOException("File '" + file + "' exists but is a directory");
168 }
169 if (file.canWrite() == false) {
170 throw new IOException("File '" + file + "' cannot be written to");
171 }
172 } else {
173 File parent = file.getParentFile();
174 if (parent != null && parent.exists() == false) {
175 if (parent.mkdirs() == false) {
176 throw new IOException("File '" + file + "' could not be created");
177 }
178 }
179 }
180 return new FileOutputStream(file);
181 }
182
183 //-----------------------------------------------------------------------
184 /**
185 * Returns a human-readable version of the file size, where the input
186 * represents a specific number of bytes.
187 *
188 * @param size the number of bytes
189 * @return a human-readable display value (includes units)
190 */
191 public static String byteCountToDisplaySize(long size) {
192 String displaySize;
193
194 if (size / ONE_GB > 0) {
195 displaySize = String.valueOf(size / ONE_GB) + " GB";
196 } else if (size / ONE_MB > 0) {
197 displaySize = String.valueOf(size / ONE_MB) + " MB";
198 } else if (size / ONE_KB > 0) {
199 displaySize = String.valueOf(size / ONE_KB) + " KB";
200 } else {
201 displaySize = String.valueOf(size) + " bytes";
202 }
203 return displaySize;
204 }
205
206 //-----------------------------------------------------------------------
207 /**
208 * Implements the same behaviour as the "touch" utility on Unix. It creates
209 * a new file with size 0 or, if the file exists already, it is opened and
210 * closed without modifying it, but updating the file date and time.
211 * <p>
212 * NOTE: As from v1.3, this method throws an IOException if the last
213 * modified date of the file cannot be set. Also, as from v1.3 this method
214 * creates parent directories if they do not exist.
215 *
216 * @param file the File to touch
217 * @throws IOException If an I/O problem occurs
218 */
219 public static void touch(File file) throws IOException {
220 if (!file.exists()) {
221 OutputStream out = openOutputStream(file);
222 IOUtils.closeQuietly(out);
223 }
224 boolean success = file.setLastModified(System.currentTimeMillis());
225 if (!success) {
226 throw new IOException("Unable to set the last modification time for " + file);
227 }
228 }
229
230 //-----------------------------------------------------------------------
231 /**
232 * Converts a Collection containing java.io.File instanced into array
233 * representation. This is to account for the difference between
234 * File.listFiles() and FileUtils.listFiles().
235 *
236 * @param files a Collection containing java.io.File instances
237 * @return an array of java.io.File
238 */
239 public static File[] convertFileCollectionToFileArray(Collection<File> files) {
240 return files.toArray(new File[files.size()]);
241 }
242
243 //-----------------------------------------------------------------------
244 /**
245 * Finds files within a given directory (and optionally its
246 * subdirectories). All files found are filtered by an IOFileFilter.
247 *
248 * @param files the collection of files found.
249 * @param directory the directory to search in.
250 * @param filter the filter to apply to files and directories.
251 */
252 private static void innerListFiles(Collection<File> files, File directory,
253 IOFileFilter filter) {
254 File[] found = directory.listFiles((FileFilter) filter);
255 if (found != null) {
256 for (int i = 0; i < found.length; i++) {
257 if (found[i].isDirectory()) {
258 innerListFiles(files, found[i], filter);
259 } else {
260 files.add(found[i]);
261 }
262 }
263 }
264 }
265
266 /**
267 * Finds files within a given directory (and optionally its
268 * subdirectories). All files found are filtered by an IOFileFilter.
269 * <p>
270 * If your search should recurse into subdirectories you can pass in
271 * an IOFileFilter for directories. You don't need to bind a
272 * DirectoryFileFilter (via logical AND) to this filter. This method does
273 * that for you.
274 * <p>
275 * An example: If you want to search through all directories called
276 * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
277 * <p>
278 * Another common usage of this method is find files in a directory
279 * tree but ignoring the directories generated CVS. You can simply pass
280 * in <code>FileFilterUtils.makeCVSAware(null)</code>.
281 *
282 * @param directory the directory to search in
283 * @param fileFilter filter to apply when finding files.
284 * @param dirFilter optional filter to apply when finding subdirectories.
285 * If this parameter is <code>null</code>, subdirectories will not be included in the
286 * search. Use TrueFileFilter.INSTANCE to match all directories.
287 * @return an collection of java.io.File with the matching files
288 * @see org.apache.commons.io.filefilter.FileFilterUtils
289 * @see org.apache.commons.io.filefilter.NameFileFilter
290 */
291 public static Collection<File> listFiles(
292 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
293 if (!directory.isDirectory()) {
294 throw new IllegalArgumentException(
295 "Parameter 'directory' is not a directory");
296 }
297 if (fileFilter == null) {
298 throw new NullPointerException("Parameter 'fileFilter' is null");
299 }
300
301 //Setup effective file filter
302 IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,
303 FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
304
305 //Setup effective directory filter
306 IOFileFilter effDirFilter;
307 if (dirFilter == null) {
308 effDirFilter = FalseFileFilter.INSTANCE;
309 } else {
310 effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
311 DirectoryFileFilter.INSTANCE);
312 }
313
314 //Find files
315 Collection<File> files = new java.util.LinkedList<File>();
316 innerListFiles(files, directory,
317 FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
318 return files;
319 }
320
321 /**
322 * Allows iteration over the files in given directory (and optionally
323 * its subdirectories).
324 * <p>
325 * All files found are filtered by an IOFileFilter. This method is
326 * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
327 *
328 * @param directory the directory to search in
329 * @param fileFilter filter to apply when finding files.
330 * @param dirFilter optional filter to apply when finding subdirectories.
331 * If this parameter is <code>null</code>, subdirectories will not be included in the
332 * search. Use TrueFileFilter.INSTANCE to match all directories.
333 * @return an iterator of java.io.File for the matching files
334 * @see org.apache.commons.io.filefilter.FileFilterUtils
335 * @see org.apache.commons.io.filefilter.NameFileFilter
336 * @since Commons IO 1.2
337 */
338 public static Iterator iterateFiles(
339 File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
340 return listFiles(directory, fileFilter, dirFilter).iterator();
341 }
342
343 //-----------------------------------------------------------------------
344 /**
345 * Converts an array of file extensions to suffixes for use
346 * with IOFileFilters.
347 *
348 * @param extensions an array of extensions. Format: {"java", "xml"}
349 * @return an array of suffixes. Format: {".java", ".xml"}
350 */
351 private static String[] toSuffixes(String[] extensions) {
352 String[] suffixes = new String[extensions.length];
353 for (int i = 0; i < extensions.length; i++) {
354 suffixes[i] = "." + extensions[i];
355 }
356 return suffixes;
357 }
358
359
360 /**
361 * Finds files within a given directory (and optionally its subdirectories)
362 * which match an array of extensions.
363 *
364 * @param directory the directory to search in
365 * @param extensions an array of extensions, ex. {"java","xml"}. If this
366 * parameter is <code>null</code>, all files are returned.
367 * @param recursive if true all subdirectories are searched as well
368 * @return an collection of java.io.File with the matching files
369 */
370 public static Collection listFiles(
371 File directory, String[] extensions, boolean recursive) {
372 IOFileFilter filter;
373 if (extensions == null) {
374 filter = TrueFileFilter.INSTANCE;
375 } else {
376 String[] suffixes = toSuffixes(extensions);
377 filter = new SuffixFileFilter(suffixes);
378 }
379 return listFiles(directory, filter,
380 (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
381 }
382
383 /**
384 * Allows iteration over the files in a given directory (and optionally
385 * its subdirectories) which match an array of extensions. This method
386 * is based on {@link #listFiles(File, String[], boolean)}.
387 *
388 * @param directory the directory to search in
389 * @param extensions an array of extensions, ex. {"java","xml"}. If this
390 * parameter is <code>null</code>, all files are returned.
391 * @param recursive if true all subdirectories are searched as well
392 * @return an iterator of java.io.File with the matching files
393 * @since Commons IO 1.2
394 */
395 public static Iterator iterateFiles(
396 File directory, String[] extensions, boolean recursive) {
397 return listFiles(directory, extensions, recursive).iterator();
398 }
399
400 //-----------------------------------------------------------------------
401 /**
402 * Compares the contents of two files to determine if they are equal or not.
403 * <p>
404 * This method checks to see if the two files are different lengths
405 * or if they point to the same file, before resorting to byte-by-byte
406 * comparison of the contents.
407 * <p>
408 * Code origin: Avalon
409 *
410 * @param file1 the first file
411 * @param file2 the second file
412 * @return true if the content of the files are equal or they both don't
413 * exist, false otherwise
414 * @throws IOException in case of an I/O error
415 */
416 public static boolean contentEquals(File file1, File file2) throws IOException {
417 boolean file1Exists = file1.exists();
418 if (file1Exists != file2.exists()) {
419 return false;
420 }
421
422 if (!file1Exists) {
423 // two not existing files are equal
424 return true;
425 }
426
427 if (file1.isDirectory() || file2.isDirectory()) {
428 // don't want to compare directory contents
429 throw new IOException("Can't compare directories, only files");
430 }
431
432 if (file1.length() != file2.length()) {
433 // lengths differ, cannot be equal
434 return false;
435 }
436
437 if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
438 // same file
439 return true;
440 }
441
442 InputStream input1 = null;
443 InputStream input2 = null;
444 try {
445 input1 = new FileInputStream(file1);
446 input2 = new FileInputStream(file2);
447 return IOUtils.contentEquals(input1, input2);
448
449 } finally {
450 IOUtils.closeQuietly(input1);
451 IOUtils.closeQuietly(input2);
452 }
453 }
454
455 //-----------------------------------------------------------------------
456 /**
457 * Convert from a <code>URL</code> to a <code>File</code>.
458 * <p>
459 * From version 1.1 this method will decode the URL.
460 * Syntax such as <code>file:///my%20docs/file.txt</code> will be
461 * correctly decoded to <code>/my docs/file.txt</code>.
462 *
463 * @param url the file URL to convert, <code>null</code> returns <code>null</code>
464 * @return the equivalent <code>File</code> object, or <code>null</code>
465 * if the URL's protocol is not <code>file</code>
466 * @throws IllegalArgumentException if the file is incorrectly encoded
467 */
468 public static File toFile(URL url) {
469 if (url == null || !url.getProtocol().equals("file")) {
470 return null;
471 } else {
472 String filename = url.getFile().replace('/', File.separatorChar);
473 int pos =0;
474 while ((pos = filename.indexOf('%', pos)) >= 0) {
475 if (pos + 2 < filename.length()) {
476 String hexStr = filename.substring(pos + 1, pos + 3);
477 char ch = (char) Integer.parseInt(hexStr, 16);
478 filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
479 }
480 }
481 return new File(filename);
482 }
483 }
484
485 /**
486 * Converts each of an array of <code>URL</code> to a <code>File</code>.
487 * <p>
488 * Returns an array of the same size as the input.
489 * If the input is <code>null</code>, an empty array is returned.
490 * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
491 * index.
492 * <p>
493 * This method will decode the URL.
494 * Syntax such as <code>file:///my%20docs/file.txt</code> will be
495 * correctly decoded to <code>/my docs/file.txt</code>.
496 *
497 * @param urls the file URLs to convert, <code>null</code> returns empty array
498 * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
499 * if there was a <code>null</code> at that index in the input array
500 * @throws IllegalArgumentException if any file is not a URL file
501 * @throws IllegalArgumentException if any file is incorrectly encoded
502 * @since Commons IO 1.1
503 */
504 public static File[] toFiles(URL[] urls) {
505 if (urls == null || urls.length == 0) {
506 return EMPTY_FILE_ARRAY;
507 }
508 File[] files = new File[urls.length];
509 for (int i = 0; i < urls.length; i++) {
510 URL url = urls[i];
511 if (url != null) {
512 if (url.getProtocol().equals("file") == false) {
513 throw new IllegalArgumentException(
514 "URL could not be converted to a File: " + url);
515 }
516 files[i] = toFile(url);
517 }
518 }
519 return files;
520 }
521
522 /**
523 * Converts each of an array of <code>File</code> to a <code>URL</code>.
524 * <p>
525 * Returns an array of the same size as the input.
526 *
527 * @param files the files to convert
528 * @return an array of URLs matching the input
529 * @throws IOException if a file cannot be converted
530 */
531 public static URL[] toURLs(File[] files) throws IOException {
532 URL[] urls = new URL[files.length];
533
534 for (int i = 0; i < urls.length; i++) {
535 urls[i] = files[i].toURL();
536 }
537
538 return urls;
539 }
540
541 //-----------------------------------------------------------------------
542 /**
543 * Copies a file to a directory preserving the file date.
544 * <p>
545 * This method copies the contents of the specified source file
546 * to a file of the same name in the specified destination directory.
547 * The destination directory is created if it does not exist.
548 * If the destination file exists, then this method will overwrite it.
549 *
550 * @param srcFile an existing file to copy, must not be <code>null</code>
551 * @param destDir the directory to place the copy in, must not be <code>null</code>
552 *
553 * @throws NullPointerException if source or destination is null
554 * @throws IOException if source or destination is invalid
555 * @throws IOException if an IO error occurs during copying
556 * @see #copyFile(File, File, boolean)
557 */
558 public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
559 copyFileToDirectory(srcFile, destDir, true);
560 }
561
562 /**
563 * Copies a file to a directory optionally preserving the file date.
564 * <p>
565 * This method copies the contents of the specified source file
566 * to a file of the same name in the specified destination directory.
567 * The destination directory is created if it does not exist.
568 * If the destination file exists, then this method will overwrite it.
569 *
570 * @param srcFile an existing file to copy, must not be <code>null</code>
571 * @param destDir the directory to place the copy in, must not be <code>null</code>
572 * @param preserveFileDate true if the file date of the copy
573 * should be the same as the original
574 *
575 * @throws NullPointerException if source or destination is <code>null</code>
576 * @throws IOException if source or destination is invalid
577 * @throws IOException if an IO error occurs during copying
578 * @see #copyFile(File, File, boolean)
579 * @since Commons IO 1.3
580 */
581 public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
582 if (destDir == null) {
583 throw new NullPointerException("Destination must not be null");
584 }
585 if (destDir.exists() && destDir.isDirectory() == false) {
586 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
587 }
588 copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
589 }
590
591 /**
592 * Copies a file to a new location preserving the file date.
593 * <p>
594 * This method copies the contents of the specified source file to the
595 * specified destination file. The directory holding the destination file is
596 * created if it does not exist. If the destination file exists, then this
597 * method will overwrite it.
598 *
599 * @param srcFile an existing file to copy, must not be <code>null</code>
600 * @param destFile the new file, must not be <code>null</code>
601 *
602 * @throws NullPointerException if source or destination is <code>null</code>
603 * @throws IOException if source or destination is invalid
604 * @throws IOException if an IO error occurs during copying
605 * @see #copyFileToDirectory(File, File)
606 */
607 public static void copyFile(File srcFile, File destFile) throws IOException {
608 copyFile(srcFile, destFile, true);
609 }
610
611 /**
612 * Copies a file to a new location.
613 * <p>
614 * This method copies the contents of the specified source file
615 * to the specified destination file.
616 * The directory holding the destination file is created if it does not exist.
617 * If the destination file exists, then this method will overwrite it.
618 *
619 * @param srcFile an existing file to copy, must not be <code>null</code>
620 * @param destFile the new file, must not be <code>null</code>
621 * @param preserveFileDate true if the file date of the copy
622 * should be the same as the original
623 *
624 * @throws NullPointerException if source or destination is <code>null</code>
625 * @throws IOException if source or destination is invalid
626 * @throws IOException if an IO error occurs during copying
627 * @see #copyFileToDirectory(File, File, boolean)
628 */
629 public static void copyFile(File srcFile, File destFile,
630 boolean preserveFileDate) throws IOException {
631 if (srcFile == null) {
632 throw new NullPointerException("Source must not be null");
633 }
634 if (destFile == null) {
635 throw new NullPointerException("Destination must not be null");
636 }
637 if (srcFile.exists() == false) {
638 throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
639 }
640 if (srcFile.isDirectory()) {
641 throw new IOException("Source '" + srcFile + "' exists but is a directory");
642 }
643 if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
644 throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
645 }
646 if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {
647 if (destFile.getParentFile().mkdirs() == false) {
648 throw new IOException("Destination '" + destFile + "' directory cannot be created");
649 }
650 }
651 if (destFile.exists() && destFile.canWrite() == false) {
652 throw new IOException("Destination '" + destFile + "' exists but is read-only");
653 }
654 doCopyFile(srcFile, destFile, preserveFileDate);
655 }
656
657 /**
658 * Internal copy file method.
659 *
660 * @param srcFile the validated source file, must not be <code>null</code>
661 * @param destFile the validated destination file, must not be <code>null</code>
662 * @param preserveFileDate whether to preserve the file date
663 * @throws IOException if an error occurs
664 */
665 private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
666 if (destFile.exists() && destFile.isDirectory()) {
667 throw new IOException("Destination '" + destFile + "' exists but is a directory");
668 }
669
670 FileChannel input = new FileInputStream(srcFile).getChannel();
671 try {
672 FileChannel output = new FileOutputStream(destFile).getChannel();
673 try {
674 output.transferFrom(input, 0, input.size());
675 } finally {
676 IOUtils.closeQuietly(output);
677 }
678 } finally {
679 IOUtils.closeQuietly(input);
680 }
681
682 if (srcFile.length() != destFile.length()) {
683 throw new IOException("Failed to copy full contents from '" +
684 srcFile + "' to '" + destFile + "'");
685 }
686 if (preserveFileDate) {
687 destFile.setLastModified(srcFile.lastModified());
688 }
689 }
690
691 //-----------------------------------------------------------------------
692 /**
693 * Copies a directory to within another directory preserving the file dates.
694 * <p>
695 * This method copies the source directory and all its contents to a
696 * directory of the same name in the specified destination directory.
697 * <p>
698 * The destination directory is created if it does not exist.
699 * If the destination directory did exist, then this method merges
700 * the source with the destination, with the source taking precedence.
701 *
702 * @param srcDir an existing directory to copy, must not be <code>null</code>
703 * @param destDir the directory to place the copy in, must not be <code>null</code>
704 *
705 * @throws NullPointerException if source or destination is <code>null</code>
706 * @throws IOException if source or destination is invalid
707 * @throws IOException if an IO error occurs during copying
708 * @since Commons IO 1.2
709 */
710 public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
711 if (srcDir == null) {
712 throw new NullPointerException("Source must not be null");
713 }
714 if (srcDir.exists() && srcDir.isDirectory() == false) {
715 throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
716 }
717 if (destDir == null) {
718 throw new NullPointerException("Destination must not be null");
719 }
720 if (destDir.exists() && destDir.isDirectory() == false) {
721 throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
722 }
723 copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
724 }
725
726 /**
727 * Copies a whole directory to a new location preserving the file dates.
728 * <p>
729 * This method copies the specified directory and all its child
730 * directories and files to the specified destination.
731 * The destination is the new location and name of the directory.
732 * <p>
733 * The destination directory is created if it does not exist.
734 * If the destination directory did exist, then this method merges
735 * the source with the destination, with the source taking precedence.
736 *
737 * @param srcDir an existing directory to copy, must not be <code>null</code>
738 * @param destDir the new directory, must not be <code>null</code>
739 *
740 * @throws NullPointerException if source or destination is <code>null</code>
741 * @throws IOException if source or destination is invalid
742 * @throws IOException if an IO error occurs during copying
743 * @since Commons IO 1.1
744 */
745 public static void copyDirectory(File srcDir, File destDir) throws IOException {
746 copyDirectory(srcDir, destDir, true);
747 }
748
749 /**
750 * Copies a whole directory to a new location.
751 * <p>
752 * This method copies the contents of the specified source directory
753 * to within the specified destination directory.
754 * <p>
755 * The destination directory is created if it does not exist.
756 * If the destination directory did exist, then this method merges
757 * the source with the destination, with the source taking precedence.
758 *
759 * @param srcDir an existing directory to copy, must not be <code>null</code>
760 * @param destDir the new directory, must not be <code>null</code>
761 * @param preserveFileDate true if the file date of the copy
762 * should be the same as the original
763 *
764 * @throws NullPointerException if source or destination is <code>null</code>
765 * @throws IOException if source or destination is invalid
766 * @throws IOException if an IO error occurs during copying
767 * @since Commons IO 1.1
768 */
769 public static void copyDirectory(File srcDir, File destDir,
770 boolean preserveFileDate) throws IOException {
771 copyDirectory(srcDir, destDir, null, preserveFileDate);
772 }
773
774 /**
775 * Copies a filtered directory to a new location preserving the file dates.
776 * <p>
777 * This method copies the contents of the specified source directory
778 * to within the specified destination directory.
779 * <p>
780 * The destination directory is created if it does not exist.
781 * If the destination directory did exist, then this method merges
782 * the source with the destination, with the source taking precedence.
783 *
784 * <h4>Example: Copy directories only</h4>
785 * <pre>
786 * // only copy the directory structure
787 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
788 * </pre>
789 *
790 * <h4>Example: Copy directories and txt files</h4>
791 * <pre>
792 * // Create a filter for ".txt" files
793 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
794 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
795 *
796 * // Create a filter for either directories or ".txt" files
797 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
798 *
799 * // Copy using the filter
800 * FileUtils.copyDirectory(srcDir, destDir, filter);
801 * </pre>
802 *
803 * @param srcDir an existing directory to copy, must not be <code>null</code>
804 * @param destDir the new directory, must not be <code>null</code>
805 * @param filter the filter to apply, null means copy all directories and files
806 * should be the same as the original
807 *
808 * @throws NullPointerException if source or destination is <code>null</code>
809 * @throws IOException if source or destination is invalid
810 * @throws IOException if an IO error occurs during copying
811 * @since Commons IO 1.4
812 */
813 public static void copyDirectory(File srcDir, File destDir,
814 FileFilter filter) throws IOException {
815 copyDirectory(srcDir, destDir, filter, true);
816 }
817
818 /**
819 * Copies a filtered directory to a new location.
820 * <p>
821 * This method copies the contents of the specified source directory
822 * to within the specified destination directory.
823 * <p>
824 * The destination directory is created if it does not exist.
825 * If the destination directory did exist, then this method merges
826 * the source with the destination, with the source taking precedence.
827 *
828 * <h4>Example: Copy directories only</h4>
829 * <pre>
830 * // only copy the directory structure
831 * FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
832 * </pre>
833 *
834 * <h4>Example: Copy directories and txt files</h4>
835 * <pre>
836 * // Create a filter for ".txt" files
837 * IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
838 * IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
839 *
840 * // Create a filter for either directories or ".txt" files
841 * FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
842 *
843 * // Copy using the filter
844 * FileUtils.copyDirectory(srcDir, destDir, filter, false);
845 * </pre>
846 *
847 * @param srcDir an existing directory to copy, must not be <code>null</code>
848 * @param destDir the new directory, must not be <code>null</code>
849 * @param filter the filter to apply, null means copy all directories and files
850 * @param preserveFileDate true if the file date of the copy
851 * should be the same as the original
852 *
853 * @throws NullPointerException if source or destination is <code>null</code>
854 * @throws IOException if source or destination is invalid
855 * @throws IOException if an IO error occurs during copying
856 * @since Commons IO 1.4
857 */
858 public static void copyDirectory(File srcDir, File destDir,
859 FileFilter filter, boolean preserveFileDate) throws IOException {
860 if (srcDir == null) {
861 throw new NullPointerException("Source must not be null");
862 }
863 if (destDir == null) {
864 throw new NullPointerException("Destination must not be null");
865 }
866 if (srcDir.exists() == false) {
867 throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
868 }
869 if (srcDir.isDirectory() == false) {
870 throw new IOException("Source '" + srcDir + "' exists but is not a directory");
871 }
872 if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
873 throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
874 }
875
876 // Cater for destination being directory within the source directory (see IO-141)
877 List<String> exclusionList = null;
878 if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
879 File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
880 if (srcFiles != null && srcFiles.length > 0) {
881 exclusionList = new ArrayList<String>(srcFiles.length);
882 for (int i = 0; i < srcFiles.length; i++) {
883 File copiedFile = new File(destDir, srcFiles[i].getName());
884 exclusionList.add(copiedFile.getCanonicalPath());
885 }
886 }
887 }
888 doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
889 }
890
891 /**
892 * Internal copy directory method.
893 *
894 * @param srcDir the validated source directory, must not be <code>null</code>
895 * @param destDir the validated destination directory, must not be <code>null</code>
896 * @param filter the filter to apply, null means copy all directories and files
897 * @param preserveFileDate whether to preserve the file date
898 * @param exclusionList List of files and directories to exclude from the copy, may be null
899 * @throws IOException if an error occurs
900 * @since Commons IO 1.1
901 */
902 private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
903 boolean preserveFileDate, List<String> exclusionList) throws IOException {
904 if (destDir.exists()) {
905 if (destDir.isDirectory() == false) {
906 throw new IOException("Destination '" + destDir + "' exists but is not a directory");
907 }
908 } else {
909 if (destDir.mkdirs() == false) {
910 throw new IOException("Destination '" + destDir + "' directory cannot be created");
911 }
912 if (preserveFileDate) {
913 destDir.setLastModified(srcDir.lastModified());
914 }
915 }
916 if (destDir.canWrite() == false) {
917 throw new IOException("Destination '" + destDir + "' cannot be written to");
918 }
919 // recurse
920 File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
921 if (files == null) { // null if security restricted
922 throw new IOException("Failed to list contents of " + srcDir);
923 }
924 for (int i = 0; i < files.length; i++) {
925 File copiedFile = new File(destDir, files[i].getName());
926 if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {
927 if (files[i].isDirectory()) {
928 doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);
929 } else {
930 doCopyFile(files[i], copiedFile, preserveFileDate);
931 }
932 }
933 }
934 }
935
936 //-----------------------------------------------------------------------
937 /**
938 * Copies bytes from the URL <code>source</code> to a file
939 * <code>destination</code>. The directories up to <code>destination</code>
940 * will be created if they don't already exist. <code>destination</code>
941 * will be overwritten if it already exists.
942 *
943 * @param source the <code>URL</code> to copy bytes from, must not be <code>null</code>
944 * @param destination the non-directory <code>File</code> to write bytes to
945 * (possibly overwriting), must not be <code>null</code>
946 * @throws IOException if <code>source</code> URL cannot be opened
947 * @throws IOException if <code>destination</code> is a directory
948 * @throws IOException if <code>destination</code> cannot be written
949 * @throws IOException if <code>destination</code> needs creating but can't be
950 * @throws IOException if an IO error occurs during copying
951 */
952 public static void copyURLToFile(URL source, File destination) throws IOException {
953 InputStream input = source.openStream();
954 try {
955 FileOutputStream output = openOutputStream(destination);
956 try {
957 IOUtils.copy(input, output);
958 } finally {
959 IOUtils.closeQuietly(output);
960 }
961 } finally {
962 IOUtils.closeQuietly(input);
963 }
964 }
965
966 //-----------------------------------------------------------------------
967 /**
968 * Deletes a directory recursively.
969 *
970 * @param directory directory to delete
971 * @throws IOException in case deletion is unsuccessful
972 */
973 public static void deleteDirectory(File directory) throws IOException {
974 if (!directory.exists()) {
975 return;
976 }
977
978 cleanDirectory(directory);
979 if (!directory.delete()) {
980 String message =
981 "Unable to delete directory " + directory + ".";
982 throw new IOException(message);
983 }
984 }
985
986 /**
987 * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
988 * <p>
989 * The difference between File.delete() and this method are:
990 * <ul>
991 * <li>A directory to be deleted does not have to be empty.</li>
992 * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
993 * </ul>
994 *
995 * @param file file or directory to delete, can be <code>null</code>
996 * @return <code>true</code> if the file or directory was deleted, otherwise
997 * <code>false</code>
998 *
999 * @since Commons IO 1.4
1000 */
1001 public static boolean deleteQuietly(File file) {
1002 if (file == null) {
1003 return false;
1004 }
1005 try {
1006 if (file.isDirectory()) {
1007 cleanDirectory(file);
1008 }
1009 } catch (Exception e) {
1010 }
1011
1012 try {
1013 return file.delete();
1014 } catch (Exception e) {
1015 return false;
1016 }
1017 }
1018
1019 /**
1020 * Cleans a directory without deleting it.
1021 *
1022 * @param directory directory to clean
1023 * @throws IOException in case cleaning is unsuccessful
1024 */
1025 public static void cleanDirectory(File directory) throws IOException {
1026 if (!directory.exists()) {
1027 String message = directory + " does not exist";
1028 throw new IllegalArgumentException(message);
1029 }
1030
1031 if (!directory.isDirectory()) {
1032 String message = directory + " is not a directory";
1033 throw new IllegalArgumentException(message);
1034 }
1035
1036 File[] files = directory.listFiles();
1037 if (files == null) { // null if security restricted
1038 throw new IOException("Failed to list contents of " + directory);
1039 }
1040
1041 IOException exception = null;
1042 for (int i = 0; i < files.length; i++) {
1043 File file = files[i];
1044 try {
1045 forceDelete(file);
1046 } catch (IOException ioe) {
1047 exception = ioe;
1048 }
1049 }
1050
1051 if (null != exception) {
1052 throw exception;
1053 }
1054 }
1055
1056 //-----------------------------------------------------------------------
1057 /**
1058 * Waits for NFS to propagate a file creation, imposing a timeout.
1059 * <p>
1060 * This method repeatedly tests {@link File#exists()} until it returns
1061 * true up to the maximum time specified in seconds.
1062 *
1063 * @param file the file to check, must not be <code>null</code>
1064 * @param seconds the maximum time in seconds to wait
1065 * @return true if file exists
1066 * @throws NullPointerException if the file is <code>null</code>
1067 */
1068 public static boolean waitFor(File file, int seconds) {
1069 int timeout = 0;
1070 int tick = 0;
1071 while (!file.exists()) {
1072 if (tick++ >= 10) {
1073 tick = 0;
1074 if (timeout++ > seconds) {
1075 return false;
1076 }
1077 }
1078 try {
1079 Thread.sleep(100);
1080 } catch (InterruptedException ignore) {
1081 // ignore exception
1082 } catch (Exception ex) {
1083 break;
1084 }
1085 }
1086 return true;
1087 }
1088
1089 //-----------------------------------------------------------------------
1090 /**
1091 * Reads the contents of a file into a String.
1092 * The file is always closed.
1093 *
1094 * @param file the file to read, must not be <code>null</code>
1095 * @param encoding the encoding to use, <code>null</code> means platform default
1096 * @return the file contents, never <code>null</code>
1097 * @throws IOException in case of an I/O error
1098 * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1099 */
1100 public static String readFileToString(File file, String encoding) throws IOException {
1101 InputStream in = null;
1102 try {
1103 in = openInputStream(file);
1104 return IOUtils.toString(in, encoding);
1105 } finally {
1106 IOUtils.closeQuietly(in);
1107 }
1108 }
1109
1110
1111 /**
1112 * Reads the contents of a file into a String using the default encoding for the VM.
1113 * The file is always closed.
1114 *
1115 * @param file the file to read, must not be <code>null</code>
1116 * @return the file contents, never <code>null</code>
1117 * @throws IOException in case of an I/O error
1118 * @since Commons IO 1.3.1
1119 */
1120 public static String readFileToString(File file) throws IOException {
1121 return readFileToString(file, null);
1122 }
1123
1124 /**
1125 * Reads the contents of a file into a byte array.
1126 * The file is always closed.
1127 *
1128 * @param file the file to read, must not be <code>null</code>
1129 * @return the file contents, never <code>null</code>
1130 * @throws IOException in case of an I/O error
1131 * @since Commons IO 1.1
1132 */
1133 public static byte[] readFileToByteArray(File file) throws IOException {
1134 InputStream in = null;
1135 try {
1136 in = openInputStream(file);
1137 return IOUtils.toByteArray(in);
1138