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