Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (C) 2007 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.io;
     18 
     19 import com.google.common.base.Preconditions;
     20 
     21 import java.io.BufferedReader;
     22 import java.io.BufferedWriter;
     23 import java.io.Closeable;
     24 import java.io.File;
     25 import java.io.FileInputStream;
     26 import java.io.FileNotFoundException;
     27 import java.io.FileOutputStream;
     28 import java.io.IOException;
     29 import java.io.InputStream;
     30 import java.io.InputStreamReader;
     31 import java.io.OutputStream;
     32 import java.io.OutputStreamWriter;
     33 import java.io.RandomAccessFile;
     34 import java.nio.MappedByteBuffer;
     35 import java.nio.channels.FileChannel;
     36 import java.nio.channels.FileChannel.MapMode;
     37 import java.nio.charset.Charset;
     38 import java.security.MessageDigest;
     39 import java.util.List;
     40 import java.util.zip.Checksum;
     41 
     42 /**
     43  * Provides utility methods for working with files.
     44  *
     45  * <p>All method parameters must be non-null unless documented otherwise.
     46  *
     47  * @author Chris Nokleberg
     48  * @since 2009.09.15 <b>tentative</b>
     49  */
     50 public final class Files {
     51 
     52   /** Maximum loop count when creating temp directories. */
     53   private static final int TEMP_DIR_ATTEMPTS = 10000;
     54 
     55   private Files() {}
     56 
     57   /**
     58    * Returns a buffered reader that reads from a file using the given
     59    * character set.
     60    *
     61    * @param file the file to read from
     62    * @param charset the character set used when writing the file
     63    * @return the buffered reader
     64    */
     65   public static BufferedReader newReader(File file, Charset charset)
     66       throws FileNotFoundException {
     67     return new BufferedReader(
     68         new InputStreamReader(new FileInputStream(file), charset));
     69   }
     70 
     71   /**
     72    * Returns a buffered writer that writes to a file using the given
     73    * character set.
     74    *
     75    * @param file the file to write to
     76    * @param charset the character set used when writing the file
     77    * @return the buffered writer
     78    */
     79   public static BufferedWriter newWriter(File file, Charset charset)
     80       throws FileNotFoundException {
     81    return new BufferedWriter(
     82         new OutputStreamWriter(new FileOutputStream(file), charset));
     83   }
     84 
     85   /**
     86    * Returns a factory that will supply instances of {@link FileInputStream}
     87    * that read from a file.
     88    *
     89    * @param file the file to read from
     90    * @return the factory
     91    */
     92   public static InputSupplier<FileInputStream> newInputStreamSupplier(
     93       final File file) {
     94     Preconditions.checkNotNull(file);
     95     return new InputSupplier<FileInputStream>() {
     96       public FileInputStream getInput() throws IOException {
     97         return new FileInputStream(file);
     98       }
     99     };
    100   }
    101 
    102   /**
    103    * Returns a factory that will supply instances of {@link FileOutputStream}
    104    * that write to a file.
    105    *
    106    * @param file the file to write to
    107    * @return the factory
    108    */
    109   public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
    110       File file) {
    111     return newOutputStreamSupplier(file, false);
    112   }
    113 
    114   /**
    115    * Returns a factory that will supply instances of {@link FileOutputStream}
    116    * that write to or append to a file.
    117    *
    118    * @param file the file to write to
    119    * @param append if true, the encoded characters will be appended to the file;
    120    *     otherwise the file is overwritten
    121    * @return the factory
    122    */
    123   public static OutputSupplier<FileOutputStream> newOutputStreamSupplier(
    124       final File file, final boolean append) {
    125     Preconditions.checkNotNull(file);
    126     return new OutputSupplier<FileOutputStream>() {
    127       public FileOutputStream getOutput() throws IOException {
    128         return new FileOutputStream(file, append);
    129       }
    130     };
    131   }
    132 
    133   /**
    134    * Returns a factory that will supply instances of
    135    * {@link InputStreamReader} that read a file using the given character set.
    136    *
    137    * @param file the file to read from
    138    * @param charset the character set used when reading the file
    139    * @return the factory
    140    */
    141   public static InputSupplier<InputStreamReader> newReaderSupplier(File file,
    142       Charset charset) {
    143     return CharStreams.newReaderSupplier(newInputStreamSupplier(file), charset);
    144   }
    145 
    146   /**
    147    * Returns a factory that will supply instances of {@link OutputStreamWriter}
    148    * that write to a file using the given character set.
    149    *
    150    * @param file the file to write to
    151    * @param charset the character set used when writing the file
    152    * @return the factory
    153    */
    154   public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
    155       Charset charset) {
    156     return newWriterSupplier(file, charset, false);
    157   }
    158 
    159   /**
    160    * Returns a factory that will supply instances of {@link OutputStreamWriter}
    161    * that write to or append to a file using the given character set.
    162    *
    163    * @param file the file to write to
    164    * @param charset the character set used when writing the file
    165    * @param append if true, the encoded characters will be appended to the file;
    166    *     otherwise the file is overwritten
    167    * @return the factory
    168    */
    169   public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file,
    170       Charset charset, boolean append) {
    171     return CharStreams.newWriterSupplier(newOutputStreamSupplier(file, append),
    172         charset);
    173   }
    174 
    175   /**
    176    * Reads all bytes from a file into a byte array.
    177    *
    178    * @param file the file to read from
    179    * @return a byte array containing all the bytes from file
    180    * @throws IllegalArgumentException if the file is bigger than the largest
    181    *     possible byte array (2^31 - 1)
    182    * @throws IOException if an I/O error occurs
    183    */
    184   public static byte[] toByteArray(File file) throws IOException {
    185     Preconditions.checkArgument(file.length() <= Integer.MAX_VALUE);
    186     if (file.length() == 0) {
    187       // Some special files are length 0 but have content nonetheless.
    188       return ByteStreams.toByteArray(newInputStreamSupplier(file));
    189     } else {
    190       // Avoid an extra allocation and copy.
    191       byte[] b = new byte[(int) file.length()];
    192       boolean threw = true;
    193       InputStream in = new FileInputStream(file);
    194       try {
    195         ByteStreams.readFully(in, b);
    196         threw = false;
    197       } finally {
    198         Closeables.close(in, threw);
    199       }
    200       return b;
    201     }
    202   }
    203 
    204   /**
    205    * Reads all characters from a file into a {@link String}, using the given
    206    * character set.
    207    *
    208    * @param file the file to read from
    209    * @param charset the character set used when reading the file
    210    * @return a string containing all the characters from the file
    211    * @throws IOException if an I/O error occurs
    212    */
    213   public static String toString(File file, Charset charset) throws IOException {
    214     return new String(toByteArray(file), charset.name());
    215   }
    216 
    217   /**
    218    * Copies to a file all bytes from an {@link InputStream} supplied by a
    219    * factory.
    220    *
    221    * @param from the input factory
    222    * @param to the destination file
    223    * @throws IOException if an I/O error occurs
    224    */
    225   public static void copy(InputSupplier<? extends InputStream> from, File to)
    226       throws IOException {
    227     ByteStreams.copy(from, newOutputStreamSupplier(to));
    228   }
    229 
    230   /**
    231    * Overwrites a file with the contents of a byte array.
    232    *
    233    * @param from the bytes to write
    234    * @param to the destination file
    235    * @throws IOException if an I/O error occurs
    236    */
    237   public static void write(byte[] from, File to) throws IOException {
    238     ByteStreams.write(from, newOutputStreamSupplier(to));
    239   }
    240 
    241   /**
    242    * Copies all bytes from a file to an {@link OutputStream} supplied by
    243    * a factory.
    244    *
    245    * @param from the source file
    246    * @param to the output factory
    247    * @throws IOException if an I/O error occurs
    248    */
    249   public static void copy(File from, OutputSupplier<? extends OutputStream> to)
    250       throws IOException {
    251     ByteStreams.copy(newInputStreamSupplier(from), to);
    252   }
    253 
    254   /**
    255    * Copies all bytes from a file to an output stream.
    256    *
    257    * @param from the source file
    258    * @param to the output stream
    259    * @throws IOException if an I/O error occurs
    260    */
    261   public static void copy(File from, OutputStream to) throws IOException {
    262     ByteStreams.copy(newInputStreamSupplier(from), to);
    263   }
    264 
    265   /**
    266    * Copies all the bytes from one file to another.
    267    *.
    268    * @param from the source file
    269    * @param to the destination file
    270    * @throws IOException if an I/O error occurs
    271    */
    272   public static void copy(File from, File to) throws IOException {
    273     copy(newInputStreamSupplier(from), to);
    274   }
    275 
    276   /**
    277    * Copies to a file all characters from a {@link Readable} and
    278    * {@link Closeable} object supplied by a factory, using the given
    279    * character set.
    280    *
    281    * @param from the readable supplier
    282    * @param to the destination file
    283    * @param charset the character set used when writing the file
    284    * @throws IOException if an I/O error occurs
    285    */
    286   public static <R extends Readable & Closeable> void copy(
    287       InputSupplier<R> from, File to, Charset charset) throws IOException {
    288     CharStreams.copy(from, newWriterSupplier(to, charset));
    289   }
    290 
    291   /**
    292    * Writes a character sequence (such as a string) to a file using the given
    293    * character set.
    294    *
    295    * @param from the character sequence to write
    296    * @param to the destination file
    297    * @param charset the character set used when writing the file
    298    * @throws IOException if an I/O error occurs
    299    */
    300   public static void write(CharSequence from, File to, Charset charset)
    301       throws IOException {
    302     write(from, to, charset, false);
    303   }
    304 
    305   /**
    306    * Appends a character sequence (such as a string) to a file using the given
    307    * character set.
    308    *
    309    * @param from the character sequence to append
    310    * @param to the destination file
    311    * @param charset the character set used when writing the file
    312    * @throws IOException if an I/O error occurs
    313    */
    314   public static void append(CharSequence from, File to, Charset charset)
    315       throws IOException {
    316     write(from, to, charset, true);
    317   }
    318 
    319   /**
    320    * Private helper method. Writes a character sequence to a file,
    321    * optionally appending.
    322    *
    323    * @param from the character sequence to append
    324    * @param to the destination file
    325    * @param charset the character set used when writing the file
    326    * @param append true to append, false to overwrite
    327    * @throws IOException if an I/O error occurs
    328    */
    329   private static void write(CharSequence from, File to, Charset charset,
    330       boolean append) throws IOException {
    331     CharStreams.write(from, newWriterSupplier(to, charset, append));
    332   }
    333 
    334   /**
    335    * Copies all characters from a file to a {@link Appendable} &
    336    * {@link Closeable} object supplied by a factory, using the given
    337    * character set.
    338    *
    339    * @param from the source file
    340    * @param charset the character set used when reading the file
    341    * @param to the appendable supplier
    342    * @throws IOException if an I/O error occurs
    343    */
    344   public static <W extends Appendable & Closeable> void copy(File from,
    345       Charset charset, OutputSupplier<W> to) throws IOException {
    346     CharStreams.copy(newReaderSupplier(from, charset), to);
    347   }
    348 
    349   /**
    350    * Copies all characters from a file to an appendable object,
    351    * using the given character set.
    352    *
    353    * @param from the source file
    354    * @param charset the character set used when reading the file
    355    * @param to the appendable object
    356    * @throws IOException if an I/O error occurs
    357    */
    358   public static void copy(File from, Charset charset, Appendable to)
    359       throws IOException {
    360     CharStreams.copy(newReaderSupplier(from, charset), to);
    361   }
    362 
    363   /**
    364    * Returns true if the files contains the same bytes.
    365    *
    366    * @throws IOException if an I/O error occurs
    367    */
    368   public static boolean equal(File file1, File file2) throws IOException {
    369     if (file1 == file2 || file1.equals(file2)) {
    370       return true;
    371     }
    372 
    373     /*
    374      * Some operating systems may return zero as the length for files
    375      * denoting system-dependent entities such as devices or pipes, in
    376      * which case we must fall back on comparing the bytes directly.
    377      */
    378     long len1 = file1.length();
    379     long len2 = file2.length();
    380     if (len1 != 0 && len2 != 0 && len1 != len2) {
    381       return false;
    382     }
    383     return ByteStreams.equal(newInputStreamSupplier(file1),
    384         newInputStreamSupplier(file2));
    385   }
    386 
    387   /**
    388    * Atomically creates a new directory somewhere beneath the system's
    389    * temporary directory (as defined by the {@code java.io.tmpdir} system
    390    * property), and returns its name.
    391    *
    392    * <p>Use this method instead of {@link File#createTempFile(String, String)}
    393    * when you wish to create a directory, not a regular file.  A common pitfall
    394    * is to call {@code createTempFile}, delete the file and create a
    395    * directory in its place, but this leads a race condition which can be
    396    * exploited to create security vulnerabilities, especially when executable
    397    * files are to be written into the directory.
    398    *
    399    * <p>This method assumes that the temporary volume is writable, has free
    400    * inodes and free blocks, and that it will not be called thousands of times
    401    * per second.
    402    *
    403    * @return the newly-created directory
    404    * @throws IllegalStateException if the directory could not be created
    405    */
    406   public static File createTempDir() {
    407     File baseDir = new File(System.getProperty("java.io.tmpdir"));
    408     String baseName = System.currentTimeMillis() + "-";
    409 
    410     for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
    411       File tempDir = new File(baseDir, baseName + counter);
    412       if (tempDir.mkdir()) {
    413         return tempDir;
    414       }
    415     }
    416     throw new IllegalStateException("Failed to create directory within "
    417         + TEMP_DIR_ATTEMPTS + " attempts (tried "
    418         + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
    419   }
    420 
    421   /**
    422    * Creates an empty file or updates the last updated timestamp on the
    423    * same as the unix command of the same name.
    424    *
    425    * @param file the file to create or update
    426    * @throws IOException if an I/O error occurs
    427    */
    428   public static void touch(File file) throws IOException {
    429     if (!file.createNewFile()
    430         && !file.setLastModified(System.currentTimeMillis())) {
    431       throw new IOException("Unable to update modification time of " + file);
    432     }
    433   }
    434 
    435   /**
    436    * Moves the file from one path to another. This method can rename a file or
    437    * move it to a different directory, like the Unix {@code mv} command.
    438    *
    439    * @param from the source file
    440    * @param to the destination file
    441    * @throws IOException if an I/O error occurs
    442    */
    443   public static void move(File from, File to) throws IOException {
    444     Preconditions.checkNotNull(to);
    445     Preconditions.checkArgument(!from.equals(to),
    446         "Source %s and destination %s must be different", from, to);
    447 
    448     if (!from.renameTo(to)) {
    449       copy(from, to);
    450       if (!from.delete()) {
    451         if (!to.delete()) {
    452           throw new IOException("Unable to delete " + to);
    453         }
    454         throw new IOException("Unable to delete " + from);
    455       }
    456     }
    457   }
    458 
    459   /**
    460    * Deletes all the files within a directory. Does not delete the
    461    * directory itself.
    462    *
    463    * <p>If the file argument is a symbolic link or there is a symbolic
    464    * link in the path leading to the directory, this method will do
    465    * nothing. Symbolic links within the directory are not followed.
    466    *
    467    * @param directory the directory to delete the contents of
    468    * @throws IllegalArgumentException if the argument is not a directory
    469    * @throws IOException if an I/O error occurs
    470    * @see #deleteRecursively
    471    */
    472   public static void deleteDirectoryContents(File directory)
    473       throws IOException {
    474     Preconditions.checkArgument(directory.isDirectory(),
    475         "Not a directory: %s", directory);
    476     // Symbolic links will have different canonical and absolute paths
    477     if (!directory.getCanonicalPath().equals(directory.getAbsolutePath())) {
    478       return;
    479     }
    480     File[] files = directory.listFiles();
    481     if (files == null) {
    482       throw new IOException("Error listing files for " + directory);
    483     }
    484     for (File file : files) {
    485       deleteRecursively(file);
    486     }
    487   }
    488 
    489   /**
    490    * Deletes a file or directory and all contents recursively.
    491    *
    492    * <p>If the file argument is a symbolic link the link will be deleted
    493    * but not the target of the link. If the argument is a directory,
    494    * symbolic links within the directory will not be followed.
    495    *
    496    * @param file the file to delete
    497    * @throws IOException if an I/O error occurs
    498    * @see #deleteDirectoryContents
    499    */
    500   public static void deleteRecursively(File file) throws IOException {
    501     if (file.isDirectory()) {
    502       deleteDirectoryContents(file);
    503     }
    504     if (!file.delete()) {
    505       throw new IOException("Failed to delete " + file);
    506     }
    507   }
    508 
    509   /**
    510    * Reads the first line from a file. The line does not include
    511    * line-termination characters, but does include other leading and
    512    * trailing whitespace.
    513    *
    514    * @param file the file to read from
    515    * @param charset the character set used when writing the file
    516    * @return the first line, or null if the file is empty
    517    * @throws IOException if an I/O error occurs
    518    */
    519   public static String readFirstLine(File file, Charset charset)
    520       throws IOException {
    521     return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset));
    522   }
    523 
    524   /**
    525    * Reads all of the lines from a file. The lines do not include
    526    * line-termination characters, but do include other leading and
    527    * trailing whitespace.
    528    *
    529    * @param file the file to read from
    530    * @param charset the character set used when writing the file
    531    * @return a mutable {@link List} containing all the lines
    532    * @throws IOException if an I/O error occurs
    533    */
    534   public static List<String> readLines(File file, Charset charset)
    535       throws IOException {
    536     return CharStreams.readLines(Files.newReaderSupplier(file, charset));
    537   }
    538 
    539   /**
    540    * Streams lines from a {@link File}, stopping when our callback returns
    541    * false, or we have read all of the lines.
    542    *
    543    * @param file the file to read from
    544    * @param charset the character set used when writing the file
    545    * @param callback the {@link LineProcessor} to use to handle the lines
    546    * @return the output of processing the lines
    547    * @throws IOException if an I/O error occurs
    548    */
    549   public static <T> T readLines(File file, Charset charset,
    550       LineProcessor<T> callback) throws IOException {
    551     return CharStreams.readLines(Files.newReaderSupplier(file, charset),
    552         callback);
    553   }
    554 
    555   /**
    556    * Process the bytes of a file.
    557    *
    558    * <p>(If this seems too complicated, maybe you're looking for
    559    * {@link #toByteArray}.)
    560    *
    561    * @param file the file to read
    562    * @param processor the object to which the bytes of the file are passed.
    563    * @return the result of the byte processor
    564    * @throws IOException if an I/O error occurs
    565    */
    566   public static <T> T readBytes(File file, ByteProcessor<T> processor)
    567       throws IOException {
    568     return ByteStreams.readBytes(newInputStreamSupplier(file), processor);
    569   }
    570 
    571   /**
    572    * Computes and returns the checksum value for a file.
    573    * The checksum object is reset when this method returns successfully.
    574    *
    575    * @param file the file to read
    576    * @param checksum the checksum object
    577    * @return the result of {@link Checksum#getValue} after updating the
    578    *     checksum object with all of the bytes in the file
    579    * @throws IOException if an I/O error occurs
    580    */
    581   public static long getChecksum(File file, Checksum checksum)
    582       throws IOException {
    583     return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum);
    584   }
    585 
    586   /**
    587    * Computes and returns the digest value for a file.
    588    * The digest object is reset when this method returns successfully.
    589    *
    590    * @param file the file to read
    591    * @param md the digest object
    592    * @return the result of {@link MessageDigest#digest()} after updating the
    593    *     digest object with all of the bytes in this file
    594    * @throws IOException if an I/O error occurs
    595    */
    596   public static byte[] getDigest(File file, MessageDigest md)
    597       throws IOException {
    598     return ByteStreams.getDigest(newInputStreamSupplier(file), md);
    599   }
    600 
    601   /**
    602    * Fully maps a file read-only in to memory as per
    603    * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
    604    *
    605    * <p>Files are mapped from offset 0 to its length.
    606    *
    607    * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
    608    *
    609    * @param file the file to map
    610    * @return a read-only buffer reflecting {@code file}
    611    * @throws FileNotFoundException if the {@code file} does not exist
    612    * @throws IOException if an I/O error occurs
    613    *
    614    * @see FileChannel#map(MapMode, long, long)
    615    * @since 2010.01.04 <b>tentative</b>
    616    */
    617   public static MappedByteBuffer map(File file) throws IOException {
    618     return map(file, MapMode.READ_ONLY);
    619   }
    620 
    621   /**
    622    * Fully maps a file in to memory as per
    623    * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
    624    * using the requested {@link MapMode}.
    625    *
    626    * <p>Files are mapped from offset 0 to its length.
    627    *
    628    * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
    629    *
    630    * @param file the file to map
    631    * @param mode the mode to use when mapping {@code file}
    632    * @return a buffer reflecting {@code file}
    633    * @throws FileNotFoundException if the {@code file} does not exist
    634    * @throws IOException if an I/O error occurs
    635    *
    636    * @see FileChannel#map(MapMode, long, long)
    637    * @since 2010.01.04 <b>tentative</b>
    638    */
    639   public static MappedByteBuffer map(File file, MapMode mode)
    640       throws IOException {
    641     if (!file.exists()) {
    642       throw new FileNotFoundException(file.toString());
    643     }
    644     return map(file, mode, file.length());
    645   }
    646 
    647   /**
    648    * Maps a file in to memory as per
    649    * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
    650    * using the requested {@link MapMode}.
    651    *
    652    * <p>Files are mapped from offset 0 to {@code size}.
    653    *
    654    * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist,
    655    * it will be created with the requested {@code size}. Thus this method is
    656    * useful for creating memory mapped files which do not yet exist.
    657    *
    658    * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
    659    *
    660    * @param file the file to map
    661    * @param mode the mode to use when mapping {@code file}
    662    * @return a buffer reflecting {@code file}
    663    * @throws IOException if an I/O error occurs
    664    *
    665    * @see FileChannel#map(MapMode, long, long)
    666    * @since 2010.01.04 <b>tentative</b>
    667    */
    668   public static MappedByteBuffer map(File file, MapMode mode, long size)
    669       throws FileNotFoundException, IOException {
    670     RandomAccessFile raf =
    671         new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw");
    672 
    673     boolean threw = true;
    674     try {
    675       MappedByteBuffer mbb = map(raf, mode, size);
    676       threw = false;
    677       return mbb;
    678     } finally {
    679       Closeables.close(raf, threw);
    680     }
    681   }
    682 
    683   private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode,
    684       long size) throws IOException {
    685     FileChannel channel = raf.getChannel();
    686 
    687     boolean threw = true;
    688     try {
    689       MappedByteBuffer mbb = channel.map(mode, 0, size);
    690       threw = false;
    691       return mbb;
    692     } finally {
    693       Closeables.close(channel, threw);
    694     }
    695   }
    696 }
    697