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