• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Date;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.zip.CRC32;
34 import java.util.zip.CheckedInputStream;
35 import java.util.zip.Checksum;
36 
37 import org.apache.commons.io.filefilter.DirectoryFileFilter;
38 import org.apache.commons.io.filefilter.FalseFileFilter;
39 import org.apache.commons.io.filefilter.FileFilterUtils;
40 import org.apache.commons.io.filefilter.IOFileFilter;
41 import org.apache.commons.io.filefilter.SuffixFileFilter;
42 import org.apache.commons.io.filefilter.TrueFileFilter;
43 import org.apache.commons.io.output.NullOutputStream;
44 
45 /**
46  * General file manipulation utilities.
47  * <p>
48  * Facilities are provided in the following areas:
49  * <ul>
50  * <li>writing to a file
51  * <li>reading from a file
52  * <li>make a directory including parent directories
53  * <li>copying files and directories
54  * <li>deleting files and directories
55  * <li>converting to and from a URL
56  * <li>listing files and directories by filter and extension
57  * <li>comparing file content
58  * <li>file last changed date
59  * <li>calculating a checksum
60  * </ul>
61  * <p>
62  * Origin of code: Excalibur, Alexandria, Commons-Utils
63  *
64  * @author <a href="mailto:burton@relativity.yi.org">Kevin A. Burton</A>
65  * @author <a href="mailto:sanders@apache.org">Scott Sanders</a>
66  * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
67  * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph.Reck</a>
68  * @author <a href="mailto:peter@apache.org">Peter Donald</a>
69  * @author <a href="mailto:jefft@apache.org">Jeff Turner</a>
70  * @author Matthew Hawthorne
71  * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
72  * @author Stephen Colebourne
73  * @author Ian Springer
74  * @author Chris Eldredge
75  * @author Jim Harrington
76  * @author Niall Pemberton
77  * @author Sandy McArthur
78  * @version $Id: FileUtils.java 610810 2008-01-10 15:04:49Z niallp $
79  */
80 public class FileUtils {
81 
82     /**
83      * Instances should NOT be constructed in standard programming.
84      */
FileUtils()85     public FileUtils() {
86         super();
87     }
88 
89     /**
90      * The number of bytes in a kilobyte.
91      */
92     public static final long ONE_KB = 1024;
93 
94     /**
95      * The number of bytes in a megabyte.
96      */
97     public static final long ONE_MB = ONE_KB * ONE_KB;
98 
99     /**
100      * The number of bytes in a gigabyte.
101      */
102     public static final long ONE_GB = ONE_KB * ONE_MB;
103 
104     /**
105      * An empty array of type <code>File</code>.
106      */
107     public static final File[] EMPTY_FILE_ARRAY = new File[0];
108 
109     //-----------------------------------------------------------------------
110     /**
111      * Opens a {@link FileInputStream} for the specified file, providing better
112      * error messages than simply calling <code>new FileInputStream(file)</code>.
113      * <p>
114      * At the end of the method either the stream will be successfully opened,
115      * or an exception will have been thrown.
116      * <p>
117      * An exception is thrown if the file does not exist.
118      * An exception is thrown if the file object exists but is a directory.
119      * An exception is thrown if the file exists but cannot be read.
120      *
121      * @param file  the file to open for input, must not be <code>null</code>
122      * @return a new {@link FileInputStream} for the specified file
123      * @throws FileNotFoundException if the file does not exist
124      * @throws IOException if the file object is a directory
125      * @throws IOException if the file cannot be read
126      * @since Commons IO 1.3
127      */
openInputStream(File file)128     public static FileInputStream openInputStream(File file) throws IOException {
129         if (file.exists()) {
130             if (file.isDirectory()) {
131                 throw new IOException("File '" + file + "' exists but is a directory");
132             }
133             if (file.canRead() == false) {
134                 throw new IOException("File '" + file + "' cannot be read");
135             }
136         } else {
137             throw new FileNotFoundException("File '" + file + "' does not exist");
138         }
139         return new FileInputStream(file);
140     }
141 
142     //-----------------------------------------------------------------------
143     /**
144      * Opens a {@link FileOutputStream} for the specified file, checking and
145      * creating the parent directory if it does not exist.
146      * <p>
147      * At the end of the method either the stream will be successfully opened,
148      * or an exception will have been thrown.
149      * <p>
150      * The parent directory will be created if it does not exist.
151      * The file will be created if it does not exist.
152      * An exception is thrown if the file object exists but is a directory.
153      * An exception is thrown if the file exists but cannot be written to.
154      * An exception is thrown if the parent directory cannot be created.
155      *
156      * @param file  the file to open for output, must not be <code>null</code>
157      * @return a new {@link FileOutputStream} for the specified file
158      * @throws IOException if the file object is a directory
159      * @throws IOException if the file cannot be written to
160      * @throws IOException if a parent directory needs creating but that fails
161      * @since Commons IO 1.3
162      */
openOutputStream(File file)163     public static FileOutputStream openOutputStream(File file) throws IOException {
164         if (file.exists()) {
165             if (file.isDirectory()) {
166                 throw new IOException("File '" + file + "' exists but is a directory");
167             }
168             if (file.canWrite() == false) {
169                 throw new IOException("File '" + file + "' cannot be written to");
170             }
171         } else {
172             File parent = file.getParentFile();
173             if (parent != null && parent.exists() == false) {
174                 if (parent.mkdirs() == false) {
175                     throw new IOException("File '" + file + "' could not be created");
176                 }
177             }
178         }
179         return new FileOutputStream(file);
180     }
181 
182     //-----------------------------------------------------------------------
183     /**
184      * Returns a human-readable version of the file size, where the input
185      * represents a specific number of bytes.
186      *
187      * @param size  the number of bytes
188      * @return a human-readable display value (includes units)
189      */
byteCountToDisplaySize(long size)190     public static String byteCountToDisplaySize(long size) {
191         String displaySize;
192 
193         if (size / ONE_GB > 0) {
194             displaySize = String.valueOf(size / ONE_GB) + " GB";
195         } else if (size / ONE_MB > 0) {
196             displaySize = String.valueOf(size / ONE_MB) + " MB";
197         } else if (size / ONE_KB > 0) {
198             displaySize = String.valueOf(size / ONE_KB) + " KB";
199         } else {
200             displaySize = String.valueOf(size) + " bytes";
201         }
202         return displaySize;
203     }
204 
205     //-----------------------------------------------------------------------
206     /**
207      * Implements the same behaviour as the "touch" utility on Unix. It creates
208      * a new file with size 0 or, if the file exists already, it is opened and
209      * closed without modifying it, but updating the file date and time.
210      * <p>
211      * NOTE: As from v1.3, this method throws an IOException if the last
212      * modified date of the file cannot be set. Also, as from v1.3 this method
213      * creates parent directories if they do not exist.
214      *
215      * @param file  the File to touch
216      * @throws IOException If an I/O problem occurs
217      */
touch(File file)218     public static void touch(File file) throws IOException {
219         if (!file.exists()) {
220             OutputStream out = openOutputStream(file);
221             IOUtils.closeQuietly(out);
222         }
223         boolean success = file.setLastModified(System.currentTimeMillis());
224         if (!success) {
225             throw new IOException("Unable to set the last modification time for " + file);
226         }
227     }
228 
229     //-----------------------------------------------------------------------
230     /**
231      * Converts a Collection containing java.io.File instanced into array
232      * representation. This is to account for the difference between
233      * File.listFiles() and FileUtils.listFiles().
234      *
235      * @param files  a Collection containing java.io.File instances
236      * @return an array of java.io.File
237      */
convertFileCollectionToFileArray(Collection<File> files)238     public static File[] convertFileCollectionToFileArray(Collection<File> files) {
239          return files.toArray(new File[files.size()]);
240     }
241 
242     //-----------------------------------------------------------------------
243     /**
244      * Finds files within a given directory (and optionally its
245      * subdirectories). All files found are filtered by an IOFileFilter.
246      *
247      * @param files the collection of files found.
248      * @param directory the directory to search in.
249      * @param filter the filter to apply to files and directories.
250      */
innerListFiles(Collection<File> files, File directory, IOFileFilter filter)251     private static void innerListFiles(Collection<File> files, File directory,
252             IOFileFilter filter) {
253         File[] found = directory.listFiles((FileFilter) filter);
254         if (found != null) {
255             for (int i = 0; i < found.length; i++) {
256                 if (found[i].isDirectory()) {
257                     innerListFiles(files, found[i], filter);
258                 } else {
259                     files.add(found[i]);
260                 }
261             }
262         }
263     }
264 
265     /**
266      * Finds files within a given directory (and optionally its
267      * subdirectories). All files found are filtered by an IOFileFilter.
268      * <p>
269      * If your search should recurse into subdirectories you can pass in
270      * an IOFileFilter for directories. You don't need to bind a
271      * DirectoryFileFilter (via logical AND) to this filter. This method does
272      * that for you.
273      * <p>
274      * An example: If you want to search through all directories called
275      * "temp" you pass in <code>FileFilterUtils.NameFileFilter("temp")</code>
276      * <p>
277      * Another common usage of this method is find files in a directory
278      * tree but ignoring the directories generated CVS. You can simply pass
279      * in <code>FileFilterUtils.makeCVSAware(null)</code>.
280      *
281      * @param directory  the directory to search in
282      * @param fileFilter  filter to apply when finding files.
283      * @param dirFilter  optional filter to apply when finding subdirectories.
284      * If this parameter is <code>null</code>, subdirectories will not be included in the
285      * search. Use TrueFileFilter.INSTANCE to match all directories.
286      * @return an collection of java.io.File with the matching files
287      * @see org.apache.commons.io.filefilter.FileFilterUtils
288      * @see org.apache.commons.io.filefilter.NameFileFilter
289      */
listFiles( File directory, IOFileFilter fileFilter, IOFileFilter dirFilter)290     public static Collection<File> listFiles(
291             File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
292         if (!directory.isDirectory()) {
293             throw new IllegalArgumentException(
294                     "Parameter 'directory' is not a directory");
295         }
296         if (fileFilter == null) {
297             throw new NullPointerException("Parameter 'fileFilter' is null");
298         }
299 
300         //Setup effective file filter
301         IOFileFilter effFileFilter = FileFilterUtils.andFileFilter(fileFilter,
302             FileFilterUtils.notFileFilter(DirectoryFileFilter.INSTANCE));
303 
304         //Setup effective directory filter
305         IOFileFilter effDirFilter;
306         if (dirFilter == null) {
307             effDirFilter = FalseFileFilter.INSTANCE;
308         } else {
309             effDirFilter = FileFilterUtils.andFileFilter(dirFilter,
310                 DirectoryFileFilter.INSTANCE);
311         }
312 
313         //Find files
314         Collection<File> files = new java.util.LinkedList<File>();
315         innerListFiles(files, directory,
316             FileFilterUtils.orFileFilter(effFileFilter, effDirFilter));
317         return files;
318     }
319 
320     /**
321      * Allows iteration over the files in given directory (and optionally
322      * its subdirectories).
323      * <p>
324      * All files found are filtered by an IOFileFilter. This method is
325      * based on {@link #listFiles(File, IOFileFilter, IOFileFilter)}.
326      *
327      * @param directory  the directory to search in
328      * @param fileFilter  filter to apply when finding files.
329      * @param dirFilter  optional filter to apply when finding subdirectories.
330      * If this parameter is <code>null</code>, subdirectories will not be included in the
331      * search. Use TrueFileFilter.INSTANCE to match all directories.
332      * @return an iterator of java.io.File for the matching files
333      * @see org.apache.commons.io.filefilter.FileFilterUtils
334      * @see org.apache.commons.io.filefilter.NameFileFilter
335      * @since Commons IO 1.2
336      */
iterateFiles( File directory, IOFileFilter fileFilter, IOFileFilter dirFilter)337     public static Iterator<File> iterateFiles(
338             File directory, IOFileFilter fileFilter, IOFileFilter dirFilter) {
339         return listFiles(directory, fileFilter, dirFilter).iterator();
340     }
341 
342     //-----------------------------------------------------------------------
343     /**
344      * Converts an array of file extensions to suffixes for use
345      * with IOFileFilters.
346      *
347      * @param extensions  an array of extensions. Format: {"java", "xml"}
348      * @return an array of suffixes. Format: {".java", ".xml"}
349      */
toSuffixes(String[] extensions)350     private static String[] toSuffixes(String[] extensions) {
351         String[] suffixes = new String[extensions.length];
352         for (int i = 0; i < extensions.length; i++) {
353             suffixes[i] = "." + extensions[i];
354         }
355         return suffixes;
356     }
357 
358 
359     /**
360      * Finds files within a given directory (and optionally its subdirectories)
361      * which match an array of extensions.
362      *
363      * @param directory  the directory to search in
364      * @param extensions  an array of extensions, ex. {"java","xml"}. If this
365      * parameter is <code>null</code>, all files are returned.
366      * @param recursive  if true all subdirectories are searched as well
367      * @return an collection of java.io.File with the matching files
368      */
listFiles( File directory, String[] extensions, boolean recursive)369     public static Collection<File> listFiles(
370             File directory, String[] extensions, boolean recursive) {
371         IOFileFilter filter;
372         if (extensions == null) {
373             filter = TrueFileFilter.INSTANCE;
374         } else {
375             String[] suffixes = toSuffixes(extensions);
376             filter = new SuffixFileFilter(suffixes);
377         }
378         return listFiles(directory, filter,
379             (recursive ? TrueFileFilter.INSTANCE : FalseFileFilter.INSTANCE));
380     }
381 
382     /**
383      * Allows iteration over the files in a given directory (and optionally
384      * its subdirectories) which match an array of extensions. This method
385      * is based on {@link #listFiles(File, String[], boolean)}.
386      *
387      * @param directory  the directory to search in
388      * @param extensions  an array of extensions, ex. {"java","xml"}. If this
389      * parameter is <code>null</code>, all files are returned.
390      * @param recursive  if true all subdirectories are searched as well
391      * @return an iterator of java.io.File with the matching files
392      * @since Commons IO 1.2
393      */
iterateFiles( File directory, String[] extensions, boolean recursive)394     public static Iterator<File> iterateFiles(
395             File directory, String[] extensions, boolean recursive) {
396         return listFiles(directory, extensions, recursive).iterator();
397     }
398 
399     //-----------------------------------------------------------------------
400     /**
401      * Compares the contents of two files to determine if they are equal or not.
402      * <p>
403      * This method checks to see if the two files are different lengths
404      * or if they point to the same file, before resorting to byte-by-byte
405      * comparison of the contents.
406      * <p>
407      * Code origin: Avalon
408      *
409      * @param file1  the first file
410      * @param file2  the second file
411      * @return true if the content of the files are equal or they both don't
412      * exist, false otherwise
413      * @throws IOException in case of an I/O error
414      */
contentEquals(File file1, File file2)415     public static boolean contentEquals(File file1, File file2) throws IOException {
416         boolean file1Exists = file1.exists();
417         if (file1Exists != file2.exists()) {
418             return false;
419         }
420 
421         if (!file1Exists) {
422             // two not existing files are equal
423             return true;
424         }
425 
426         if (file1.isDirectory() || file2.isDirectory()) {
427             // don't want to compare directory contents
428             throw new IOException("Can't compare directories, only files");
429         }
430 
431         if (file1.length() != file2.length()) {
432             // lengths differ, cannot be equal
433             return false;
434         }
435 
436         if (file1.getCanonicalFile().equals(file2.getCanonicalFile())) {
437             // same file
438             return true;
439         }
440 
441         InputStream input1 = null;
442         InputStream input2 = null;
443         try {
444             input1 = new FileInputStream(file1);
445             input2 = new FileInputStream(file2);
446             return IOUtils.contentEquals(input1, input2);
447 
448         } finally {
449             IOUtils.closeQuietly(input1);
450             IOUtils.closeQuietly(input2);
451         }
452     }
453 
454     //-----------------------------------------------------------------------
455     /**
456      * Convert from a <code>URL</code> to a <code>File</code>.
457      * <p>
458      * From version 1.1 this method will decode the URL.
459      * Syntax such as <code>file:///my%20docs/file.txt</code> will be
460      * correctly decoded to <code>/my docs/file.txt</code>.
461      *
462      * @param url  the file URL to convert, <code>null</code> returns <code>null</code>
463      * @return the equivalent <code>File</code> object, or <code>null</code>
464      *  if the URL's protocol is not <code>file</code>
465      * @throws IllegalArgumentException if the file is incorrectly encoded
466      */
toFile(URL url)467     public static File toFile(URL url) {
468         if (url == null || !url.getProtocol().equals("file")) {
469             return null;
470         } else {
471             String filename = url.getFile().replace('/', File.separatorChar);
472             int pos =0;
473             while ((pos = filename.indexOf('%', pos)) >= 0) {
474                 if (pos + 2 < filename.length()) {
475                     String hexStr = filename.substring(pos + 1, pos + 3);
476                     char ch = (char) Integer.parseInt(hexStr, 16);
477                     filename = filename.substring(0, pos) + ch + filename.substring(pos + 3);
478                 }
479             }
480             return new File(filename);
481         }
482     }
483 
484     /**
485      * Converts each of an array of <code>URL</code> to a <code>File</code>.
486      * <p>
487      * Returns an array of the same size as the input.
488      * If the input is <code>null</code>, an empty array is returned.
489      * If the input contains <code>null</code>, the output array contains <code>null</code> at the same
490      * index.
491      * <p>
492      * This method will decode the URL.
493      * Syntax such as <code>file:///my%20docs/file.txt</code> will be
494      * correctly decoded to <code>/my docs/file.txt</code>.
495      *
496      * @param urls  the file URLs to convert, <code>null</code> returns empty array
497      * @return a non-<code>null</code> array of Files matching the input, with a <code>null</code> item
498      *  if there was a <code>null</code> at that index in the input array
499      * @throws IllegalArgumentException if any file is not a URL file
500      * @throws IllegalArgumentException if any file is incorrectly encoded
501      * @since Commons IO 1.1
502      */
toFiles(URL[] urls)503     public static File[] toFiles(URL[] urls) {
504         if (urls == null || urls.length == 0) {
505             return EMPTY_FILE_ARRAY;
506         }
507         File[] files = new File[urls.length];
508         for (int i = 0; i < urls.length; i++) {
509             URL url = urls[i];
510             if (url != null) {
511                 if (url.getProtocol().equals("file") == false) {
512                     throw new IllegalArgumentException(
513                             "URL could not be converted to a File: " + url);
514                 }
515                 files[i] = toFile(url);
516             }
517         }
518         return files;
519     }
520 
521     /**
522      * Converts each of an array of <code>File</code> to a <code>URL</code>.
523      * <p>
524      * Returns an array of the same size as the input.
525      *
526      * @param files  the files to convert
527      * @return an array of URLs matching the input
528      * @throws IOException if a file cannot be converted
529      */
toURLs(File[] files)530     public static URL[] toURLs(File[] files) throws IOException {
531         URL[] urls = new URL[files.length];
532 
533         for (int i = 0; i < urls.length; i++) {
534             urls[i] = files[i].toURI().toURL();
535         }
536 
537         return urls;
538     }
539 
540     //-----------------------------------------------------------------------
541     /**
542      * Copies a file to a directory preserving the file date.
543      * <p>
544      * This method copies the contents of the specified source file
545      * to a file of the same name in the specified destination directory.
546      * The destination directory is created if it does not exist.
547      * If the destination file exists, then this method will overwrite it.
548      *
549      * @param srcFile  an existing file to copy, must not be <code>null</code>
550      * @param destDir  the directory to place the copy in, must not be <code>null</code>
551      *
552      * @throws NullPointerException if source or destination is null
553      * @throws IOException if source or destination is invalid
554      * @throws IOException if an IO error occurs during copying
555      * @see #copyFile(File, File, boolean)
556      */
copyFileToDirectory(File srcFile, File destDir)557     public static void copyFileToDirectory(File srcFile, File destDir) throws IOException {
558         copyFileToDirectory(srcFile, destDir, true);
559     }
560 
561     /**
562      * Copies a file to a directory optionally preserving the file date.
563      * <p>
564      * This method copies the contents of the specified source file
565      * to a file of the same name in the specified destination directory.
566      * The destination directory is created if it does not exist.
567      * If the destination file exists, then this method will overwrite it.
568      *
569      * @param srcFile  an existing file to copy, must not be <code>null</code>
570      * @param destDir  the directory to place the copy in, must not be <code>null</code>
571      * @param preserveFileDate  true if the file date of the copy
572      *  should be the same as the original
573      *
574      * @throws NullPointerException if source or destination is <code>null</code>
575      * @throws IOException if source or destination is invalid
576      * @throws IOException if an IO error occurs during copying
577      * @see #copyFile(File, File, boolean)
578      * @since Commons IO 1.3
579      */
copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate)580     public static void copyFileToDirectory(File srcFile, File destDir, boolean preserveFileDate) throws IOException {
581         if (destDir == null) {
582             throw new NullPointerException("Destination must not be null");
583         }
584         if (destDir.exists() && destDir.isDirectory() == false) {
585             throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
586         }
587         copyFile(srcFile, new File(destDir, srcFile.getName()), preserveFileDate);
588     }
589 
590     /**
591      * Copies a file to a new location preserving the file date.
592      * <p>
593      * This method copies the contents of the specified source file to the
594      * specified destination file. The directory holding the destination file is
595      * created if it does not exist. If the destination file exists, then this
596      * method will overwrite it.
597      *
598      * @param srcFile  an existing file to copy, must not be <code>null</code>
599      * @param destFile  the new file, must not be <code>null</code>
600      *
601      * @throws NullPointerException if source or destination is <code>null</code>
602      * @throws IOException if source or destination is invalid
603      * @throws IOException if an IO error occurs during copying
604      * @see #copyFileToDirectory(File, File)
605      */
copyFile(File srcFile, File destFile)606     public static void copyFile(File srcFile, File destFile) throws IOException {
607         copyFile(srcFile, destFile, true);
608     }
609 
610     /**
611      * Copies a file to a new location.
612      * <p>
613      * This method copies the contents of the specified source file
614      * to the specified destination file.
615      * The directory holding the destination file is created if it does not exist.
616      * If the destination file exists, then this method will overwrite it.
617      *
618      * @param srcFile  an existing file to copy, must not be <code>null</code>
619      * @param destFile  the new file, must not be <code>null</code>
620      * @param preserveFileDate  true if the file date of the copy
621      *  should be the same as the original
622      *
623      * @throws NullPointerException if source or destination is <code>null</code>
624      * @throws IOException if source or destination is invalid
625      * @throws IOException if an IO error occurs during copying
626      * @see #copyFileToDirectory(File, File, boolean)
627      */
copyFile(File srcFile, File destFile, boolean preserveFileDate)628     public static void copyFile(File srcFile, File destFile,
629             boolean preserveFileDate) throws IOException {
630         if (srcFile == null) {
631             throw new NullPointerException("Source must not be null");
632         }
633         if (destFile == null) {
634             throw new NullPointerException("Destination must not be null");
635         }
636         if (srcFile.exists() == false) {
637             throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
638         }
639         if (srcFile.isDirectory()) {
640             throw new IOException("Source '" + srcFile + "' exists but is a directory");
641         }
642         if (srcFile.getCanonicalPath().equals(destFile.getCanonicalPath())) {
643             throw new IOException("Source '" + srcFile + "' and destination '" + destFile + "' are the same");
644         }
645         if (destFile.getParentFile() != null && destFile.getParentFile().exists() == false) {
646             if (destFile.getParentFile().mkdirs() == false) {
647                 throw new IOException("Destination '" + destFile + "' directory cannot be created");
648             }
649         }
650         if (destFile.exists() && destFile.canWrite() == false) {
651             throw new IOException("Destination '" + destFile + "' exists but is read-only");
652         }
653         doCopyFile(srcFile, destFile, preserveFileDate);
654     }
655 
656     /**
657      * Internal copy file method.
658      *
659      * @param srcFile  the validated source file, must not be <code>null</code>
660      * @param destFile  the validated destination file, must not be <code>null</code>
661      * @param preserveFileDate  whether to preserve the file date
662      * @throws IOException if an error occurs
663      */
doCopyFile(File srcFile, File destFile, boolean preserveFileDate)664     private static void doCopyFile(File srcFile, File destFile, boolean preserveFileDate) throws IOException {
665         if (destFile.exists() && destFile.isDirectory()) {
666             throw new IOException("Destination '" + destFile + "' exists but is a directory");
667         }
668 
669         FileInputStream input = new FileInputStream(srcFile);
670         try {
671             FileOutputStream output = new FileOutputStream(destFile);
672             try {
673                 IOUtils.copy(input, output);
674             } finally {
675                 IOUtils.closeQuietly(output);
676             }
677         } finally {
678             IOUtils.closeQuietly(input);
679         }
680 
681         if (srcFile.length() != destFile.length()) {
682             throw new IOException("Failed to copy full contents from '" +
683                     srcFile + "' to '" + destFile + "'");
684         }
685         if (preserveFileDate) {
686             destFile.setLastModified(srcFile.lastModified());
687         }
688     }
689 
690     //-----------------------------------------------------------------------
691     /**
692      * Copies a directory to within another directory preserving the file dates.
693      * <p>
694      * This method copies the source directory and all its contents to a
695      * directory of the same name in the specified destination directory.
696      * <p>
697      * The destination directory is created if it does not exist.
698      * If the destination directory did exist, then this method merges
699      * the source with the destination, with the source taking precedence.
700      *
701      * @param srcDir  an existing directory to copy, must not be <code>null</code>
702      * @param destDir  the directory to place the copy in, must not be <code>null</code>
703      *
704      * @throws NullPointerException if source or destination is <code>null</code>
705      * @throws IOException if source or destination is invalid
706      * @throws IOException if an IO error occurs during copying
707      * @since Commons IO 1.2
708      */
copyDirectoryToDirectory(File srcDir, File destDir)709     public static void copyDirectoryToDirectory(File srcDir, File destDir) throws IOException {
710         if (srcDir == null) {
711             throw new NullPointerException("Source must not be null");
712         }
713         if (srcDir.exists() && srcDir.isDirectory() == false) {
714             throw new IllegalArgumentException("Source '" + destDir + "' is not a directory");
715         }
716         if (destDir == null) {
717             throw new NullPointerException("Destination must not be null");
718         }
719         if (destDir.exists() && destDir.isDirectory() == false) {
720             throw new IllegalArgumentException("Destination '" + destDir + "' is not a directory");
721         }
722         copyDirectory(srcDir, new File(destDir, srcDir.getName()), true);
723     }
724 
725     /**
726      * Copies a whole directory to a new location preserving the file dates.
727      * <p>
728      * This method copies the specified directory and all its child
729      * directories and files to the specified destination.
730      * The destination is the new location and name of the directory.
731      * <p>
732      * The destination directory is created if it does not exist.
733      * If the destination directory did exist, then this method merges
734      * the source with the destination, with the source taking precedence.
735      *
736      * @param srcDir  an existing directory to copy, must not be <code>null</code>
737      * @param destDir  the new directory, must not be <code>null</code>
738      *
739      * @throws NullPointerException if source or destination is <code>null</code>
740      * @throws IOException if source or destination is invalid
741      * @throws IOException if an IO error occurs during copying
742      * @since Commons IO 1.1
743      */
copyDirectory(File srcDir, File destDir)744     public static void copyDirectory(File srcDir, File destDir) throws IOException {
745         copyDirectory(srcDir, destDir, true);
746     }
747 
748     /**
749      * Copies a whole directory to a new location.
750      * <p>
751      * This method copies the contents of the specified source directory
752      * to within the specified destination directory.
753      * <p>
754      * The destination directory is created if it does not exist.
755      * If the destination directory did exist, then this method merges
756      * the source with the destination, with the source taking precedence.
757      *
758      * @param srcDir  an existing directory to copy, must not be <code>null</code>
759      * @param destDir  the new directory, must not be <code>null</code>
760      * @param preserveFileDate  true if the file date of the copy
761      *  should be the same as the original
762      *
763      * @throws NullPointerException if source or destination is <code>null</code>
764      * @throws IOException if source or destination is invalid
765      * @throws IOException if an IO error occurs during copying
766      * @since Commons IO 1.1
767      */
copyDirectory(File srcDir, File destDir, boolean preserveFileDate)768     public static void copyDirectory(File srcDir, File destDir,
769             boolean preserveFileDate) throws IOException {
770         copyDirectory(srcDir, destDir, null, preserveFileDate);
771     }
772 
773     /**
774      * Copies a filtered directory to a new location preserving the file dates.
775      * <p>
776      * This method copies the contents of the specified source directory
777      * to within the specified destination directory.
778      * <p>
779      * The destination directory is created if it does not exist.
780      * If the destination directory did exist, then this method merges
781      * the source with the destination, with the source taking precedence.
782      *
783      * <h4>Example: Copy directories only</h4>
784      *  <pre>
785      *  // only copy the directory structure
786      *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY);
787      *  </pre>
788      *
789      * <h4>Example: Copy directories and txt files</h4>
790      *  <pre>
791      *  // Create a filter for ".txt" files
792      *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
793      *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
794      *
795      *  // Create a filter for either directories or ".txt" files
796      *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
797      *
798      *  // Copy using the filter
799      *  FileUtils.copyDirectory(srcDir, destDir, filter);
800      *  </pre>
801      *
802      * @param srcDir  an existing directory to copy, must not be <code>null</code>
803      * @param destDir  the new directory, must not be <code>null</code>
804      * @param filter  the filter to apply, null means copy all directories and files
805      *  should be the same as the original
806      *
807      * @throws NullPointerException if source or destination is <code>null</code>
808      * @throws IOException if source or destination is invalid
809      * @throws IOException if an IO error occurs during copying
810      * @since Commons IO 1.4
811      */
copyDirectory(File srcDir, File destDir, FileFilter filter)812     public static void copyDirectory(File srcDir, File destDir,
813             FileFilter filter) throws IOException {
814         copyDirectory(srcDir, destDir, filter, true);
815     }
816 
817     /**
818      * Copies a filtered directory to a new location.
819      * <p>
820      * This method copies the contents of the specified source directory
821      * to within the specified destination directory.
822      * <p>
823      * The destination directory is created if it does not exist.
824      * If the destination directory did exist, then this method merges
825      * the source with the destination, with the source taking precedence.
826      *
827      * <h4>Example: Copy directories only</h4>
828      *  <pre>
829      *  // only copy the directory structure
830      *  FileUtils.copyDirectory(srcDir, destDir, DirectoryFileFilter.DIRECTORY, false);
831      *  </pre>
832      *
833      * <h4>Example: Copy directories and txt files</h4>
834      *  <pre>
835      *  // Create a filter for ".txt" files
836      *  IOFileFilter txtSuffixFilter = FileFilterUtils.suffixFileFilter(".txt");
837      *  IOFileFilter txtFiles = FileFilterUtils.andFileFilter(FileFileFilter.FILE, txtSuffixFilter);
838      *
839      *  // Create a filter for either directories or ".txt" files
840      *  FileFilter filter = FileFilterUtils.orFileFilter(DirectoryFileFilter.DIRECTORY, txtFiles);
841      *
842      *  // Copy using the filter
843      *  FileUtils.copyDirectory(srcDir, destDir, filter, false);
844      *  </pre>
845      *
846      * @param srcDir  an existing directory to copy, must not be <code>null</code>
847      * @param destDir  the new directory, must not be <code>null</code>
848      * @param filter  the filter to apply, null means copy all directories and files
849      * @param preserveFileDate  true if the file date of the copy
850      *  should be the same as the original
851      *
852      * @throws NullPointerException if source or destination is <code>null</code>
853      * @throws IOException if source or destination is invalid
854      * @throws IOException if an IO error occurs during copying
855      * @since Commons IO 1.4
856      */
copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate)857     public static void copyDirectory(File srcDir, File destDir,
858             FileFilter filter, boolean preserveFileDate) throws IOException {
859         if (srcDir == null) {
860             throw new NullPointerException("Source must not be null");
861         }
862         if (destDir == null) {
863             throw new NullPointerException("Destination must not be null");
864         }
865         if (srcDir.exists() == false) {
866             throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
867         }
868         if (srcDir.isDirectory() == false) {
869             throw new IOException("Source '" + srcDir + "' exists but is not a directory");
870         }
871         if (srcDir.getCanonicalPath().equals(destDir.getCanonicalPath())) {
872             throw new IOException("Source '" + srcDir + "' and destination '" + destDir + "' are the same");
873         }
874 
875         // Cater for destination being directory within the source directory (see IO-141)
876         List<String> exclusionList = null;
877         if (destDir.getCanonicalPath().startsWith(srcDir.getCanonicalPath())) {
878             File[] srcFiles = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
879             if (srcFiles != null && srcFiles.length > 0) {
880                 exclusionList = new ArrayList<String>(srcFiles.length);
881                 for (int i = 0; i < srcFiles.length; i++) {
882                     File copiedFile = new File(destDir, srcFiles[i].getName());
883                     exclusionList.add(copiedFile.getCanonicalPath());
884                 }
885             }
886         }
887         doCopyDirectory(srcDir, destDir, filter, preserveFileDate, exclusionList);
888     }
889 
890     /**
891      * Internal copy directory method.
892      *
893      * @param srcDir  the validated source directory, must not be <code>null</code>
894      * @param destDir  the validated destination directory, must not be <code>null</code>
895      * @param filter  the filter to apply, null means copy all directories and files
896      * @param preserveFileDate  whether to preserve the file date
897      * @param exclusionList  List of files and directories to exclude from the copy, may be null
898      * @throws IOException if an error occurs
899      * @since Commons IO 1.1
900      */
doCopyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate, List<String> exclusionList)901     private static void doCopyDirectory(File srcDir, File destDir, FileFilter filter,
902             boolean preserveFileDate, List<String> exclusionList) throws IOException {
903         if (destDir.exists()) {
904             if (destDir.isDirectory() == false) {
905                 throw new IOException("Destination '" + destDir + "' exists but is not a directory");
906             }
907         } else {
908             if (destDir.mkdirs() == false) {
909                 throw new IOException("Destination '" + destDir + "' directory cannot be created");
910             }
911             if (preserveFileDate) {
912                 destDir.setLastModified(srcDir.lastModified());
913             }
914         }
915         if (destDir.canWrite() == false) {
916             throw new IOException("Destination '" + destDir + "' cannot be written to");
917         }
918         // recurse
919         File[] files = filter == null ? srcDir.listFiles() : srcDir.listFiles(filter);
920         if (files == null) {  // null if security restricted
921             throw new IOException("Failed to list contents of " + srcDir);
922         }
923         for (int i = 0; i < files.length; i++) {
924             File copiedFile = new File(destDir, files[i].getName());
925             if (exclusionList == null || !exclusionList.contains(files[i].getCanonicalPath())) {
926                 if (files[i].isDirectory()) {
927                     doCopyDirectory(files[i], copiedFile, filter, preserveFileDate, exclusionList);
928                 } else {
929                     doCopyFile(files[i], copiedFile, preserveFileDate);
930                 }
931             }
932         }
933     }
934 
935     //-----------------------------------------------------------------------
936     /**
937      * Copies bytes from the URL <code>source</code> to a file
938      * <code>destination</code>. The directories up to <code>destination</code>
939      * will be created if they don't already exist. <code>destination</code>
940      * will be overwritten if it already exists.
941      *
942      * @param source  the <code>URL</code> to copy bytes from, must not be <code>null</code>
943      * @param destination  the non-directory <code>File</code> to write bytes to
944      *  (possibly overwriting), must not be <code>null</code>
945      * @throws IOException if <code>source</code> URL cannot be opened
946      * @throws IOException if <code>destination</code> is a directory
947      * @throws IOException if <code>destination</code> cannot be written
948      * @throws IOException if <code>destination</code> needs creating but can't be
949      * @throws IOException if an IO error occurs during copying
950      */
copyURLToFile(URL source, File destination)951     public static void copyURLToFile(URL source, File destination) throws IOException {
952         InputStream input = source.openStream();
953         try {
954             FileOutputStream output = openOutputStream(destination);
955             try {
956                 IOUtils.copy(input, output);
957             } finally {
958                 IOUtils.closeQuietly(output);
959             }
960         } finally {
961             IOUtils.closeQuietly(input);
962         }
963     }
964 
965     //-----------------------------------------------------------------------
966     /**
967      * Deletes a directory recursively.
968      *
969      * @param directory  directory to delete
970      * @throws IOException in case deletion is unsuccessful
971      */
deleteDirectory(File directory)972     public static void deleteDirectory(File directory) throws IOException {
973         if (!directory.exists()) {
974             return;
975         }
976 
977         cleanDirectory(directory);
978         if (!directory.delete()) {
979             String message =
980                 "Unable to delete directory " + directory + ".";
981             throw new IOException(message);
982         }
983     }
984 
985     /**
986      * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories.
987      * <p>
988      * The difference between File.delete() and this method are:
989      * <ul>
990      * <li>A directory to be deleted does not have to be empty.</li>
991      * <li>No exceptions are thrown when a file or directory cannot be deleted.</li>
992      * </ul>
993      *
994      * @param file  file or directory to delete, can be <code>null</code>
995      * @return <code>true</code> if the file or directory was deleted, otherwise
996      * <code>false</code>
997      *
998      * @since Commons IO 1.4
999      */
deleteQuietly(File file)1000     public static boolean deleteQuietly(File file) {
1001         if (file == null) {
1002             return false;
1003         }
1004         try {
1005             if (file.isDirectory()) {
1006                 cleanDirectory(file);
1007             }
1008         } catch (Exception e) {
1009         }
1010 
1011         try {
1012             return file.delete();
1013         } catch (Exception e) {
1014             return false;
1015         }
1016     }
1017 
1018     /**
1019      * Cleans a directory without deleting it.
1020      *
1021      * @param directory directory to clean
1022      * @throws IOException in case cleaning is unsuccessful
1023      */
cleanDirectory(File directory)1024     public static void cleanDirectory(File directory) throws IOException {
1025         if (!directory.exists()) {
1026             String message = directory + " does not exist";
1027             throw new IllegalArgumentException(message);
1028         }
1029 
1030         if (!directory.isDirectory()) {
1031             String message = directory + " is not a directory";
1032             throw new IllegalArgumentException(message);
1033         }
1034 
1035         File[] files = directory.listFiles();
1036         if (files == null) {  // null if security restricted
1037             throw new IOException("Failed to list contents of " + directory);
1038         }
1039 
1040         IOException exception = null;
1041         for (int i = 0; i < files.length; i++) {
1042             File file = files[i];
1043             try {
1044                 forceDelete(file);
1045             } catch (IOException ioe) {
1046                 exception = ioe;
1047             }
1048         }
1049 
1050         if (null != exception) {
1051             throw exception;
1052         }
1053     }
1054 
1055     //-----------------------------------------------------------------------
1056     /**
1057      * Waits for NFS to propagate a file creation, imposing a timeout.
1058      * <p>
1059      * This method repeatedly tests {@link File#exists()} until it returns
1060      * true up to the maximum time specified in seconds.
1061      *
1062      * @param file  the file to check, must not be <code>null</code>
1063      * @param seconds  the maximum time in seconds to wait
1064      * @return true if file exists
1065      * @throws NullPointerException if the file is <code>null</code>
1066      */
waitFor(File file, int seconds)1067     public static boolean waitFor(File file, int seconds) {
1068         int timeout = 0;
1069         int tick = 0;
1070         while (!file.exists()) {
1071             if (tick++ >= 10) {
1072                 tick = 0;
1073                 if (timeout++ > seconds) {
1074                     return false;
1075                 }
1076             }
1077             try {
1078                 Thread.sleep(100);
1079             } catch (InterruptedException ignore) {
1080                 // ignore exception
1081             } catch (Exception ex) {
1082                 break;
1083             }
1084         }
1085         return true;
1086     }
1087 
1088     //-----------------------------------------------------------------------
1089     /**
1090      * Reads the contents of a file into a String.
1091      * The file is always closed.
1092      *
1093      * @param file  the file to read, must not be <code>null</code>
1094      * @param encoding  the encoding to use, <code>null</code> means platform default
1095      * @return the file contents, never <code>null</code>
1096      * @throws IOException in case of an I/O error
1097      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1098      */
readFileToString(File file, String encoding)1099     public static String readFileToString(File file, String encoding) throws IOException {
1100         InputStream in = null;
1101         try {
1102             in = openInputStream(file);
1103             return IOUtils.toString(in, encoding);
1104         } finally {
1105             IOUtils.closeQuietly(in);
1106         }
1107     }
1108 
1109 
1110     /**
1111      * Reads the contents of a file into a String using the default encoding for the VM.
1112      * The file is always closed.
1113      *
1114      * @param file  the file to read, must not be <code>null</code>
1115      * @return the file contents, never <code>null</code>
1116      * @throws IOException in case of an I/O error
1117      * @since Commons IO 1.3.1
1118      */
readFileToString(File file)1119     public static String readFileToString(File file) throws IOException {
1120         return readFileToString(file, null);
1121     }
1122 
1123     /**
1124      * Reads the contents of a file into a byte array.
1125      * The file is always closed.
1126      *
1127      * @param file  the file to read, must not be <code>null</code>
1128      * @return the file contents, never <code>null</code>
1129      * @throws IOException in case of an I/O error
1130      * @since Commons IO 1.1
1131      */
readFileToByteArray(File file)1132     public static byte[] readFileToByteArray(File file) throws IOException {
1133         InputStream in = null;
1134         try {
1135             in = openInputStream(file);
1136             return IOUtils.toByteArray(in);
1137         } finally {
1138             IOUtils.closeQuietly(in);
1139         }
1140     }
1141 
1142     /**
1143      * Reads the contents of a file line by line to a List of Strings.
1144      * The file is always closed.
1145      *
1146      * @param file  the file to read, must not be <code>null</code>
1147      * @param encoding  the encoding to use, <code>null</code> means platform default
1148      * @return the list of Strings representing each line in the file, never <code>null</code>
1149      * @throws IOException in case of an I/O error
1150      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1151      * @since Commons IO 1.1
1152      */
readLines(File file, String encoding)1153     public static List<String> readLines(File file, String encoding) throws IOException {
1154         InputStream in = null;
1155         try {
1156             in = openInputStream(file);
1157             return IOUtils.readLines(in, encoding);
1158         } finally {
1159             IOUtils.closeQuietly(in);
1160         }
1161     }
1162 
1163     /**
1164      * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM.
1165      * The file is always closed.
1166      *
1167      * @param file  the file to read, must not be <code>null</code>
1168      * @return the list of Strings representing each line in the file, never <code>null</code>
1169      * @throws IOException in case of an I/O error
1170      * @since Commons IO 1.3
1171      */
readLines(File file)1172     public static List<String> readLines(File file) throws IOException {
1173         return readLines(file, null);
1174     }
1175 
1176     /**
1177      * Returns an Iterator for the lines in a <code>File</code>.
1178      * <p>
1179      * This method opens an <code>InputStream</code> for the file.
1180      * When you have finished with the iterator you should close the stream
1181      * to free internal resources. This can be done by calling the
1182      * {@link LineIterator#close()} or
1183      * {@link LineIterator#closeQuietly(LineIterator)} method.
1184      * <p>
1185      * The recommended usage pattern is:
1186      * <pre>
1187      * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
1188      * try {
1189      *   while (it.hasNext()) {
1190      *     String line = it.nextLine();
1191      *     /// do something with line
1192      *   }
1193      * } finally {
1194      *   LineIterator.closeQuietly(iterator);
1195      * }
1196      * </pre>
1197      * <p>
1198      * If an exception occurs during the creation of the iterator, the
1199      * underlying stream is closed.
1200      *
1201      * @param file  the file to open for input, must not be <code>null</code>
1202      * @param encoding  the encoding to use, <code>null</code> means platform default
1203      * @return an Iterator of the lines in the file, never <code>null</code>
1204      * @throws IOException in case of an I/O error (file closed)
1205      * @since Commons IO 1.2
1206      */
lineIterator(File file, String encoding)1207     public static LineIterator lineIterator(File file, String encoding) throws IOException {
1208         InputStream in = null;
1209         try {
1210             in = openInputStream(file);
1211             return IOUtils.lineIterator(in, encoding);
1212         } catch (IOException ex) {
1213             IOUtils.closeQuietly(in);
1214             throw ex;
1215         } catch (RuntimeException ex) {
1216             IOUtils.closeQuietly(in);
1217             throw ex;
1218         }
1219     }
1220 
1221     /**
1222      * Returns an Iterator for the lines in a <code>File</code> using the default encoding for the VM.
1223      *
1224      * @param file  the file to open for input, must not be <code>null</code>
1225      * @return an Iterator of the lines in the file, never <code>null</code>
1226      * @throws IOException in case of an I/O error (file closed)
1227      * @since Commons IO 1.3
1228      * @see #lineIterator(File, String)
1229      */
lineIterator(File file)1230     public static LineIterator lineIterator(File file) throws IOException {
1231         return lineIterator(file, null);
1232     }
1233 
1234     //-----------------------------------------------------------------------
1235     /**
1236      * Writes a String to a file creating the file if it does not exist.
1237      *
1238      * NOTE: As from v1.3, the parent directories of the file will be created
1239      * if they do not exist.
1240      *
1241      * @param file  the file to write
1242      * @param data  the content to write to the file
1243      * @param encoding  the encoding to use, <code>null</code> means platform default
1244      * @throws IOException in case of an I/O error
1245      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1246      */
writeStringToFile(File file, String data, String encoding)1247     public static void writeStringToFile(File file, String data, String encoding) throws IOException {
1248         OutputStream out = null;
1249         try {
1250             out = openOutputStream(file);
1251             IOUtils.write(data, out, encoding);
1252         } finally {
1253             IOUtils.closeQuietly(out);
1254         }
1255     }
1256 
1257     /**
1258      * Writes a String to a file creating the file if it does not exist using the default encoding for the VM.
1259      *
1260      * @param file  the file to write
1261      * @param data  the content to write to the file
1262      * @throws IOException in case of an I/O error
1263      */
writeStringToFile(File file, String data)1264     public static void writeStringToFile(File file, String data) throws IOException {
1265         writeStringToFile(file, data, null);
1266     }
1267 
1268     /**
1269      * Writes a byte array to a file creating the file if it does not exist.
1270      * <p>
1271      * NOTE: As from v1.3, the parent directories of the file will be created
1272      * if they do not exist.
1273      *
1274      * @param file  the file to write to
1275      * @param data  the content to write to the file
1276      * @throws IOException in case of an I/O error
1277      * @since Commons IO 1.1
1278      */
writeByteArrayToFile(File file, byte[] data)1279     public static void writeByteArrayToFile(File file, byte[] data) throws IOException {
1280         OutputStream out = null;
1281         try {
1282             out = openOutputStream(file);
1283             out.write(data);
1284         } finally {
1285             IOUtils.closeQuietly(out);
1286         }
1287     }
1288 
1289     /**
1290      * Writes the <code>toString()</code> value of each item in a collection to
1291      * the specified <code>File</code> line by line.
1292      * The specified character encoding and the default line ending will be used.
1293      * <p>
1294      * NOTE: As from v1.3, the parent directories of the file will be created
1295      * if they do not exist.
1296      *
1297      * @param file  the file to write to
1298      * @param encoding  the encoding to use, <code>null</code> means platform default
1299      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1300      * @throws IOException in case of an I/O error
1301      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1302      * @since Commons IO 1.1
1303      */
writeLines(File file, String encoding, Collection<Object> lines)1304     public static void writeLines(File file, String encoding, Collection<Object> lines) throws IOException {
1305         writeLines(file, encoding, lines, null);
1306     }
1307 
1308     /**
1309      * Writes the <code>toString()</code> value of each item in a collection to
1310      * the specified <code>File</code> line by line.
1311      * The default VM encoding and the default line ending will be used.
1312      *
1313      * @param file  the file to write to
1314      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1315      * @throws IOException in case of an I/O error
1316      * @since Commons IO 1.3
1317      */
writeLines(File file, Collection<Object> lines)1318     public static void writeLines(File file, Collection<Object> lines) throws IOException {
1319         writeLines(file, null, lines, null);
1320     }
1321 
1322     /**
1323      * Writes the <code>toString()</code> value of each item in a collection to
1324      * the specified <code>File</code> line by line.
1325      * The specified character encoding and the line ending will be used.
1326      * <p>
1327      * NOTE: As from v1.3, the parent directories of the file will be created
1328      * if they do not exist.
1329      *
1330      * @param file  the file to write to
1331      * @param encoding  the encoding to use, <code>null</code> means platform default
1332      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1333      * @param lineEnding  the line separator to use, <code>null</code> is system default
1334      * @throws IOException in case of an I/O error
1335      * @throws java.io.UnsupportedEncodingException if the encoding is not supported by the VM
1336      * @since Commons IO 1.1
1337      */
writeLines(File file, String encoding, Collection<Object> lines, String lineEnding)1338     public static void writeLines(File file, String encoding, Collection<Object> lines, String lineEnding) throws IOException {
1339         OutputStream out = null;
1340         try {
1341             out = openOutputStream(file);
1342             IOUtils.writeLines(lines, lineEnding, out, encoding);
1343         } finally {
1344             IOUtils.closeQuietly(out);
1345         }
1346     }
1347 
1348     /**
1349      * Writes the <code>toString()</code> value of each item in a collection to
1350      * the specified <code>File</code> line by line.
1351      * The default VM encoding and the specified line ending will be used.
1352      *
1353      * @param file  the file to write to
1354      * @param lines  the lines to write, <code>null</code> entries produce blank lines
1355      * @param lineEnding  the line separator to use, <code>null</code> is system default
1356      * @throws IOException in case of an I/O error
1357      * @since Commons IO 1.3
1358      */
writeLines(File file, Collection<Object> lines, String lineEnding)1359     public static void writeLines(File file, Collection<Object> lines, String lineEnding) throws IOException {
1360         writeLines(file, null, lines, lineEnding);
1361     }
1362 
1363     //-----------------------------------------------------------------------
1364     /**
1365      * Deletes a file. If file is a directory, delete it and all sub-directories.
1366      * <p>
1367      * The difference between File.delete() and this method are:
1368      * <ul>
1369      * <li>A directory to be deleted does not have to be empty.</li>
1370      * <li>You get exceptions when a file or directory cannot be deleted.
1371      *      (java.io.File methods returns a boolean)</li>
1372      * </ul>
1373      *
1374      * @param file  file or directory to delete, must not be <code>null</code>
1375      * @throws NullPointerException if the directory is <code>null</code>
1376      * @throws FileNotFoundException if the file was not found
1377      * @throws IOException in case deletion is unsuccessful
1378      */
forceDelete(File file)1379     public static void forceDelete(File file) throws IOException {
1380         if (file.isDirectory()) {
1381             deleteDirectory(file);
1382         } else {
1383             boolean filePresent = file.exists();
1384             if (!file.delete()) {
1385                 if (!filePresent){
1386                     throw new FileNotFoundException("File does not exist: " + file);
1387                 }
1388                 String message =
1389                     "Unable to delete file: " + file;
1390                 throw new IOException(message);
1391             }
1392         }
1393     }
1394 
1395     /**
1396      * Schedules a file to be deleted when JVM exits.
1397      * If file is directory delete it and all sub-directories.
1398      *
1399      * @param file  file or directory to delete, must not be <code>null</code>
1400      * @throws NullPointerException if the file is <code>null</code>
1401      * @throws IOException in case deletion is unsuccessful
1402      */
forceDeleteOnExit(File file)1403     public static void forceDeleteOnExit(File file) throws IOException {
1404         if (file.isDirectory()) {
1405             deleteDirectoryOnExit(file);
1406         } else {
1407             file.deleteOnExit();
1408         }
1409     }
1410 
1411     /**
1412      * Schedules a directory recursively for deletion on JVM exit.
1413      *
1414      * @param directory  directory to delete, must not be <code>null</code>
1415      * @throws NullPointerException if the directory is <code>null</code>
1416      * @throws IOException in case deletion is unsuccessful
1417      */
deleteDirectoryOnExit(File directory)1418     private static void deleteDirectoryOnExit(File directory) throws IOException {
1419         if (!directory.exists()) {
1420             return;
1421         }
1422 
1423         cleanDirectoryOnExit(directory);
1424         directory.deleteOnExit();
1425     }
1426 
1427     /**
1428      * Cleans a directory without deleting it.
1429      *
1430      * @param directory  directory to clean, must not be <code>null</code>
1431      * @throws NullPointerException if the directory is <code>null</code>
1432      * @throws IOException in case cleaning is unsuccessful
1433      */
cleanDirectoryOnExit(File directory)1434     private static void cleanDirectoryOnExit(File directory) throws IOException {
1435         if (!directory.exists()) {
1436             String message = directory + " does not exist";
1437             throw new IllegalArgumentException(message);
1438         }
1439 
1440         if (!directory.isDirectory()) {
1441             String message = directory + " is not a directory";
1442             throw new IllegalArgumentException(message);
1443         }
1444 
1445         File[] files = directory.listFiles();
1446         if (files == null) {  // null if security restricted
1447             throw new IOException("Failed to list contents of " + directory);
1448         }
1449 
1450         IOException exception = null;
1451         for (int i = 0; i < files.length; i++) {
1452             File file = files[i];
1453             try {
1454                 forceDeleteOnExit(file);
1455             } catch (IOException ioe) {
1456                 exception = ioe;
1457             }
1458         }
1459 
1460         if (null != exception) {
1461             throw exception;
1462         }
1463     }
1464 
1465     /**
1466      * Makes a directory, including any necessary but nonexistent parent
1467      * directories. If there already exists a file with specified name or
1468      * the directory cannot be created then an exception is thrown.
1469      *
1470      * @param directory  directory to create, must not be <code>null</code>
1471      * @throws NullPointerException if the directory is <code>null</code>
1472      * @throws IOException if the directory cannot be created
1473      */
forceMkdir(File directory)1474     public static void forceMkdir(File directory) throws IOException {
1475         if (directory.exists()) {
1476             if (directory.isFile()) {
1477                 String message =
1478                     "File "
1479                         + directory
1480                         + " exists and is "
1481                         + "not a directory. Unable to create directory.";
1482                 throw new IOException(message);
1483             }
1484         } else {
1485             if (!directory.mkdirs()) {
1486                 String message =
1487                     "Unable to create directory " + directory;
1488                 throw new IOException(message);
1489             }
1490         }
1491     }
1492 
1493     //-----------------------------------------------------------------------
1494     /**
1495      * Counts the size of a directory recursively (sum of the length of all files).
1496      *
1497      * @param directory  directory to inspect, must not be <code>null</code>
1498      * @return size of directory in bytes, 0 if directory is security restricted
1499      * @throws NullPointerException if the directory is <code>null</code>
1500      */
sizeOfDirectory(File directory)1501     public static long sizeOfDirectory(File directory) {
1502         if (!directory.exists()) {
1503             String message = directory + " does not exist";
1504             throw new IllegalArgumentException(message);
1505         }
1506 
1507         if (!directory.isDirectory()) {
1508             String message = directory + " is not a directory";
1509             throw new IllegalArgumentException(message);
1510         }
1511 
1512         long size = 0;
1513 
1514         File[] files = directory.listFiles();
1515         if (files == null) {  // null if security restricted
1516             return 0L;
1517         }
1518         for (int i = 0; i < files.length; i++) {
1519             File file = files[i];
1520 
1521             if (file.isDirectory()) {
1522                 size += sizeOfDirectory(file);
1523             } else {
1524                 size += file.length();
1525             }
1526         }
1527 
1528         return size;
1529     }
1530 
1531     //-----------------------------------------------------------------------
1532     /**
1533      * Tests if the specified <code>File</code> is newer than the reference
1534      * <code>File</code>.
1535      *
1536      * @param file  the <code>File</code> of which the modification date must
1537      * be compared, must not be <code>null</code>
1538      * @param reference  the <code>File</code> of which the modification date
1539      * is used, must not be <code>null</code>
1540      * @return true if the <code>File</code> exists and has been modified more
1541      * recently than the reference <code>File</code>
1542      * @throws IllegalArgumentException if the file is <code>null</code>
1543      * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
1544      */
isFileNewer(File file, File reference)1545      public static boolean isFileNewer(File file, File reference) {
1546         if (reference == null) {
1547             throw new IllegalArgumentException("No specified reference file");
1548         }
1549         if (!reference.exists()) {
1550             throw new IllegalArgumentException("The reference file '"
1551                     + file + "' doesn't exist");
1552         }
1553         return isFileNewer(file, reference.lastModified());
1554     }
1555 
1556     /**
1557      * Tests if the specified <code>File</code> is newer than the specified
1558      * <code>Date</code>.
1559      *
1560      * @param file  the <code>File</code> of which the modification date
1561      * must be compared, must not be <code>null</code>
1562      * @param date  the date reference, must not be <code>null</code>
1563      * @return true if the <code>File</code> exists and has been modified
1564      * after the given <code>Date</code>.
1565      * @throws IllegalArgumentException if the file is <code>null</code>
1566      * @throws IllegalArgumentException if the date is <code>null</code>
1567      */
isFileNewer(File file, Date date)1568     public static boolean isFileNewer(File file, Date date) {
1569         if (date == null) {
1570             throw new IllegalArgumentException("No specified date");
1571         }
1572         return isFileNewer(file, date.getTime());
1573     }
1574 
1575     /**
1576      * Tests if the specified <code>File</code> is newer than the specified
1577      * time reference.
1578      *
1579      * @param file  the <code>File</code> of which the modification date must
1580      * be compared, must not be <code>null</code>
1581      * @param timeMillis  the time reference measured in milliseconds since the
1582      * epoch (00:00:00 GMT, January 1, 1970)
1583      * @return true if the <code>File</code> exists and has been modified after
1584      * the given time reference.
1585      * @throws IllegalArgumentException if the file is <code>null</code>
1586      */
isFileNewer(File file, long timeMillis)1587      public static boolean isFileNewer(File file, long timeMillis) {
1588         if (file == null) {
1589             throw new IllegalArgumentException("No specified file");
1590         }
1591         if (!file.exists()) {
1592             return false;
1593         }
1594         return file.lastModified() > timeMillis;
1595     }
1596 
1597 
1598     //-----------------------------------------------------------------------
1599     /**
1600      * Tests if the specified <code>File</code> is older than the reference
1601      * <code>File</code>.
1602      *
1603      * @param file  the <code>File</code> of which the modification date must
1604      * be compared, must not be <code>null</code>
1605      * @param reference  the <code>File</code> of which the modification date
1606      * is used, must not be <code>null</code>
1607      * @return true if the <code>File</code> exists and has been modified before
1608      * the reference <code>File</code>
1609      * @throws IllegalArgumentException if the file is <code>null</code>
1610      * @throws IllegalArgumentException if the reference file is <code>null</code> or doesn't exist
1611      */
isFileOlder(File file, File reference)1612      public static boolean isFileOlder(File file, File reference) {
1613         if (reference == null) {
1614             throw new IllegalArgumentException("No specified reference file");
1615         }
1616         if (!reference.exists()) {
1617             throw new IllegalArgumentException("The reference file '"
1618                     + file + "' doesn't exist");
1619         }
1620         return isFileOlder(file, reference.lastModified());
1621     }
1622 
1623     /**
1624      * Tests if the specified <code>File</code> is older than the specified
1625      * <code>Date</code>.
1626      *
1627      * @param file  the <code>File</code> of which the modification date
1628      * must be compared, must not be <code>null</code>
1629      * @param date  the date reference, must not be <code>null</code>
1630      * @return true if the <code>File</code> exists and has been modified
1631      * before the given <code>Date</code>.
1632      * @throws IllegalArgumentException if the file is <code>null</code>
1633      * @throws IllegalArgumentException if the date is <code>null</code>
1634      */
isFileOlder(File file, Date date)1635     public static boolean isFileOlder(File file, Date date) {
1636         if (date == null) {
1637             throw new IllegalArgumentException("No specified date");
1638         }
1639         return isFileOlder(file, date.getTime());
1640     }
1641 
1642     /**
1643      * Tests if the specified <code>File</code> is older than the specified
1644      * time reference.
1645      *
1646      * @param file  the <code>File</code> of which the modification date must
1647      * be compared, must not be <code>null</code>
1648      * @param timeMillis  the time reference measured in milliseconds since the
1649      * epoch (00:00:00 GMT, January 1, 1970)
1650      * @return true if the <code>File</code> exists and has been modified before
1651      * the given time reference.
1652      * @throws IllegalArgumentException if the file is <code>null</code>
1653      */
isFileOlder(File file, long timeMillis)1654      public static boolean isFileOlder(File file, long timeMillis) {
1655         if (file == null) {
1656             throw new IllegalArgumentException("No specified file");
1657         }
1658         if (!file.exists()) {
1659             return false;
1660         }
1661         return file.lastModified() < timeMillis;
1662     }
1663 
1664     //-----------------------------------------------------------------------
1665     /**
1666      * Computes the checksum of a file using the CRC32 checksum routine.
1667      * The value of the checksum is returned.
1668      *
1669      * @param file  the file to checksum, must not be <code>null</code>
1670      * @return the checksum value
1671      * @throws NullPointerException if the file or checksum is <code>null</code>
1672      * @throws IllegalArgumentException if the file is a directory
1673      * @throws IOException if an IO error occurs reading the file
1674      * @since Commons IO 1.3
1675      */
checksumCRC32(File file)1676     public static long checksumCRC32(File file) throws IOException {
1677         CRC32 crc = new CRC32();
1678         checksum(file, crc);
1679         return crc.getValue();
1680     }
1681 
1682     /**
1683      * Computes the checksum of a file using the specified checksum object.
1684      * Multiple files may be checked using one <code>Checksum</code> instance
1685      * if desired simply by reusing the same checksum object.
1686      * For example:
1687      * <pre>
1688      *   long csum = FileUtils.checksum(file, new CRC32()).getValue();
1689      * </pre>
1690      *
1691      * @param file  the file to checksum, must not be <code>null</code>
1692      * @param checksum  the checksum object to be used, must not be <code>null</code>
1693      * @return the checksum specified, updated with the content of the file
1694      * @throws NullPointerException if the file or checksum is <code>null</code>
1695      * @throws IllegalArgumentException if the file is a directory
1696      * @throws IOException if an IO error occurs reading the file
1697      * @since Commons IO 1.3
1698      */
checksum(File file, Checksum checksum)1699     public static Checksum checksum(File file, Checksum checksum) throws IOException {
1700         if (file.isDirectory()) {
1701             throw new IllegalArgumentException("Checksums can't be computed on directories");
1702         }
1703         InputStream in = null;
1704         try {
1705             in = new CheckedInputStream(new FileInputStream(file), checksum);
1706             IOUtils.copy(in, new NullOutputStream());
1707         } finally {
1708             IOUtils.closeQuietly(in);
1709         }
1710         return checksum;
1711     }
1712 
1713     /**
1714      * Moves a directory.
1715      * <p>
1716      * When the destination directory is on another file system, do a "copy and delete".
1717      *
1718      * @param srcDir the directory to be moved
1719      * @param destDir the destination directory
1720      * @throws NullPointerException if source or destination is <code>null</code>
1721      * @throws IOException if source or destination is invalid
1722      * @throws IOException if an IO error occurs moving the file
1723      * @since Commons IO 1.4
1724      */
moveDirectory(File srcDir, File destDir)1725     public static void moveDirectory(File srcDir, File destDir) throws IOException {
1726         if (srcDir == null) {
1727             throw new NullPointerException("Source must not be null");
1728         }
1729         if (destDir == null) {
1730             throw new NullPointerException("Destination must not be null");
1731         }
1732         if (!srcDir.exists()) {
1733             throw new FileNotFoundException("Source '" + srcDir + "' does not exist");
1734         }
1735         if (!srcDir.isDirectory()) {
1736             throw new IOException("Source '" + srcDir + "' is not a directory");
1737         }
1738         if (destDir.exists()) {
1739             throw new IOException("Destination '" + destDir + "' already exists");
1740         }
1741         boolean rename = srcDir.renameTo(destDir);
1742         if (!rename) {
1743             copyDirectory( srcDir, destDir );
1744             deleteDirectory( srcDir );
1745             if (srcDir.exists()) {
1746                 throw new IOException("Failed to delete original directory '" + srcDir +
1747                         "' after copy to '" + destDir + "'");
1748             }
1749         }
1750     }
1751 
1752     /**
1753      * Moves a directory to another directory.
1754      *
1755      * @param src the file to be moved
1756      * @param destDir the destination file
1757      * @param createDestDir If <code>true</code> create the destination directory,
1758      * otherwise if <code>false</code> throw an IOException
1759      * @throws NullPointerException if source or destination is <code>null</code>
1760      * @throws IOException if source or destination is invalid
1761      * @throws IOException if an IO error occurs moving the file
1762      * @since Commons IO 1.4
1763      */
moveDirectoryToDirectory(File src, File destDir, boolean createDestDir)1764     public static void moveDirectoryToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
1765         if (src == null) {
1766             throw new NullPointerException("Source must not be null");
1767         }
1768         if (destDir == null) {
1769             throw new NullPointerException("Destination directory must not be null");
1770         }
1771         if (!destDir.exists() && createDestDir) {
1772             destDir.mkdirs();
1773         }
1774         if (!destDir.exists()) {
1775             throw new FileNotFoundException("Destination directory '" + destDir +
1776                     "' does not exist [createDestDir=" + createDestDir +"]");
1777         }
1778         if (!destDir.isDirectory()) {
1779             throw new IOException("Destination '" + destDir + "' is not a directory");
1780         }
1781         moveDirectory(src, new File(destDir, src.getName()));
1782 
1783     }
1784 
1785     /**
1786      * Moves a file.
1787      * <p>
1788      * When the destination file is on another file system, do a "copy and delete".
1789      *
1790      * @param srcFile the file to be moved
1791      * @param destFile the destination file
1792      * @throws NullPointerException if source or destination is <code>null</code>
1793      * @throws IOException if source or destination is invalid
1794      * @throws IOException if an IO error occurs moving the file
1795      * @since Commons IO 1.4
1796      */
moveFile(File srcFile, File destFile)1797     public static void moveFile(File srcFile, File destFile) throws IOException {
1798         if (srcFile == null) {
1799             throw new NullPointerException("Source must not be null");
1800         }
1801         if (destFile == null) {
1802             throw new NullPointerException("Destination must not be null");
1803         }
1804         if (!srcFile.exists()) {
1805             throw new FileNotFoundException("Source '" + srcFile + "' does not exist");
1806         }
1807         if (srcFile.isDirectory()) {
1808             throw new IOException("Source '" + srcFile + "' is a directory");
1809         }
1810         if (destFile.exists()) {
1811             throw new IOException("Destination '" + destFile + "' already exists");
1812         }
1813         if (destFile.isDirectory()) {
1814             throw new IOException("Destination '" + destFile + "' is a directory");
1815         }
1816         boolean rename = srcFile.renameTo(destFile);
1817         if (!rename) {
1818             copyFile( srcFile, destFile );
1819             if (!srcFile.delete()) {
1820                 FileUtils.deleteQuietly(destFile);
1821                 throw new IOException("Failed to delete original file '" + srcFile +
1822                         "' after copy to '" + destFile + "'");
1823             }
1824         }
1825     }
1826 
1827     /**
1828      * Moves a file to a directory.
1829      *
1830      * @param srcFile the file to be moved
1831      * @param destDir the destination file
1832      * @param createDestDir If <code>true</code> create the destination directory,
1833      * otherwise if <code>false</code> throw an IOException
1834      * @throws NullPointerException if source or destination is <code>null</code>
1835      * @throws IOException if source or destination is invalid
1836      * @throws IOException if an IO error occurs moving the file
1837      * @since Commons IO 1.4
1838      */
moveFileToDirectory(File srcFile, File destDir, boolean createDestDir)1839     public static void moveFileToDirectory(File srcFile, File destDir, boolean createDestDir) throws IOException {
1840         if (srcFile == null) {
1841             throw new NullPointerException("Source must not be null");
1842         }
1843         if (destDir == null) {
1844             throw new NullPointerException("Destination directory must not be null");
1845         }
1846         if (!destDir.exists() && createDestDir) {
1847             destDir.mkdirs();
1848         }
1849         if (!destDir.exists()) {
1850             throw new FileNotFoundException("Destination directory '" + destDir +
1851                     "' does not exist [createDestDir=" + createDestDir +"]");
1852         }
1853         if (!destDir.isDirectory()) {
1854             throw new IOException("Destination '" + destDir + "' is not a directory");
1855         }
1856         moveFile(srcFile, new File(destDir, srcFile.getName()));
1857     }
1858 
1859     /**
1860      * Moves a file or directory to the destination directory.
1861      * <p>
1862      * When the destination is on another file system, do a "copy and delete".
1863      *
1864      * @param src the file or directory to be moved
1865      * @param destDir the destination directory
1866      * @param createDestDir If <code>true</code> create the destination directory,
1867      * otherwise if <code>false</code> throw an IOException
1868      * @throws NullPointerException if source or destination is <code>null</code>
1869      * @throws IOException if source or destination is invalid
1870      * @throws IOException if an IO error occurs moving the file
1871      * @since Commons IO 1.4
1872      */
moveToDirectory(File src, File destDir, boolean createDestDir)1873     public static void moveToDirectory(File src, File destDir, boolean createDestDir) throws IOException {
1874         if (src == null) {
1875             throw new NullPointerException("Source must not be null");
1876         }
1877         if (destDir == null) {
1878             throw new NullPointerException("Destination must not be null");
1879         }
1880         if (!src.exists()) {
1881             throw new FileNotFoundException("Source '" + src + "' does not exist");
1882         }
1883         if (src.isDirectory()) {
1884             moveDirectoryToDirectory(src, destDir, createDestDir);
1885         } else {
1886             moveFileToDirectory(src, destDir, createDestDir);
1887         }
1888     }
1889 
1890 }
1891