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 if (parent == null) {
450 /*
451 * The given directory is a filesystem root. All zero of its ancestors
452 * exist. This doesn't mean that the root itself exists -- consider x:\ on
453 * a Windows machine without such a drive -- or even that the caller can
454 * create it, but this method makes no such guarantees even for non-root
455 * files.
456 */
457 return;
458 }
459 parent.mkdirs();
460 if (!parent.isDirectory()) {
461 throw new IOException("Unable to create parent directories of " + file);
462 }
463 }
464
465 /**
466 * Moves the file from one path to another. This method can rename a file or
467 * move it to a different directory, like the Unix {@code mv} command.
468 *
469 * @param from the source file
470 * @param to the destination file
471 * @throws IOException if an I/O error occurs
472 */
473 public static void move(File from, File to) throws IOException {
474 Preconditions.checkNotNull(to);
475 Preconditions.checkArgument(!from.equals(to),
476 "Source %s and destination %s must be different", from, to);
477
478 if (!from.renameTo(to)) {
479 copy(from, to);
480 if (!from.delete()) {
481 if (!to.delete()) {
482 throw new IOException("Unable to delete " + to);
483 }
484 throw new IOException("Unable to delete " + from);
485 }
486 }
487 }
488
489 /**
490 * Deletes all the files within a directory. Does not delete the
491 * directory itself.
492 *
493 * <p>If the file argument is a symbolic link or there is a symbolic
494 * link in the path leading to the directory, this method will do
495 * nothing. Symbolic links within the directory are not followed.
496 *
497 * @param directory the directory to delete the contents of
498 * @throws IllegalArgumentException if the argument is not a directory
499 * @throws IOException if an I/O error occurs
500 * @see #deleteRecursively
501 */
502 public static void deleteDirectoryContents(File directory)
503 throws IOException {
504 Preconditions.checkArgument(directory.isDirectory(),
505 "Not a directory: %s", directory);
506 // Symbolic links will have different canonical and absolute paths
507 if (!directory.getCanonicalPath().equals(directory.getAbsolutePath())) {
508 return;
509 }
510 File[] files = directory.listFiles();
511 if (files == null) {
512 throw new IOException("Error listing files for " + directory);
513 }
514 for (File file : files) {
515 deleteRecursively(file);
516 }
517 }
518
519 /**
520 * Deletes a file or directory and all contents recursively.
521 *
522 * <p>If the file argument is a symbolic link the link will be deleted
523 * but not the target of the link. If the argument is a directory,
524 * symbolic links within the directory will not be followed.
525 *
526 * @param file the file to delete
527 * @throws IOException if an I/O error occurs
528 * @see #deleteDirectoryContents
529 */
530 public static void deleteRecursively(File file) throws IOException {
531 if (file.isDirectory()) {
532 deleteDirectoryContents(file);
533 }
534 if (!file.delete()) {
535 throw new IOException("Failed to delete " + file);
536 }
537 }
538
539 /**
540 * Reads the first line from a file. The line does not include
541 * line-termination characters, but does include other leading and
542 * trailing whitespace.
543 *
544 * @param file the file to read from
545 * @param charset the character set used when writing the file
546 * @return the first line, or null if the file is empty
547 * @throws IOException if an I/O error occurs
548 */
549 public static String readFirstLine(File file, Charset charset)
550 throws IOException {
551 return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset));
552 }
553
554 /**
555 * Reads all of the lines from a file. The lines do not include
556 * line-termination characters, but do include other leading and
557 * trailing whitespace.
558 *
559 * @param file the file to read from
560 * @param charset the character set used when writing the file
561 * @return a mutable {@link List} containing all the lines
562 * @throws IOException if an I/O error occurs
563 */
564 public static List<String> readLines(File file, Charset charset)
565 throws IOException {
566 return CharStreams.readLines(Files.newReaderSupplier(file, charset));
567 }
568
569 /**
570 * Streams lines from a {@link File}, stopping when our callback returns
571 * false, or we have read all of the lines.
572 *
573 * @param file the file to read from
574 * @param charset the character set used when writing the file
575 * @param callback the {@link LineProcessor} to use to handle the lines
576 * @return the output of processing the lines
577 * @throws IOException if an I/O error occurs
578 */
579 public static <T> T readLines(File file, Charset charset,
580 LineProcessor<T> callback) throws IOException {
581 return CharStreams.readLines(Files.newReaderSupplier(file, charset),
582 callback);
583 }
584
585 /**
586 * Process the bytes of a file.
587 *
588 * <p>(If this seems too complicated, maybe you're looking for
589 * {@link #toByteArray}.)
590 *
591 * @param file the file to read
592 * @param processor the object to which the bytes of the file are passed.
593 * @return the result of the byte processor
594 * @throws IOException if an I/O error occurs
595 */
596 public static <T> T readBytes(File file, ByteProcessor<T> processor)
597 throws IOException {
598 return ByteStreams.readBytes(newInputStreamSupplier(file), processor);
599 }
600
601 /**
602 * Computes and returns the checksum value for a file.
603 * The checksum object is reset when this method returns successfully.
604 *
605 * @param file the file to read
606 * @param checksum the checksum object
607 * @return the result of {@link Checksum#getValue} after updating the
608 * checksum object with all of the bytes in the file
609 * @throws IOException if an I/O error occurs
610 */
611 public static long getChecksum(File file, Checksum checksum)
612 throws IOException {
613 return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum);
614 }
615
616 /**
617 * Computes and returns the digest value for a file.
618 * The digest object is reset when this method returns successfully.
619 *
620 * @param file the file to read
621 * @param md the digest object
622 * @return the result of {@link MessageDigest#digest()} after updating the
623 * digest object with all of the bytes in this file
624 * @throws IOException if an I/O error occurs
625 */
626 public static byte[] getDigest(File file, MessageDigest md)
627 throws IOException {
628 return ByteStreams.getDigest(newInputStreamSupplier(file), md);
629 }
630
631 /**
632 * Fully maps a file read-only in to memory as per
633 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
634 *
635 * <p>Files are mapped from offset 0 to its length.
636 *
637 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
638 *
639 * @param file the file to map
640 * @return a read-only buffer reflecting {@code file}
641 * @throws FileNotFoundException if the {@code file} does not exist
642 * @throws IOException if an I/O error occurs
643 *
644 * @see FileChannel#map(MapMode, long, long)
645 * @since 2
646 */
647 public static MappedByteBuffer map(File file) throws IOException {
648 return map(file, MapMode.READ_ONLY);
649 }
650
651 /**
652 * Fully maps a file in to memory as per
653 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
654 * using the requested {@link MapMode}.
655 *
656 * <p>Files are mapped from offset 0 to its length.
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 FileNotFoundException if the {@code file} does not exist
664 * @throws IOException if an I/O error occurs
665 *
666 * @see FileChannel#map(MapMode, long, long)
667 * @since 2
668 */
669 public static MappedByteBuffer map(File file, MapMode mode)
670 throws IOException {
671 if (!file.exists()) {
672 throw new FileNotFoundException(file.toString());
673 }
674 return map(file, mode, file.length());
675 }
676
677 /**
678 * Maps a file in to memory as per
679 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
680 * using the requested {@link MapMode}.
681 *
682 * <p>Files are mapped from offset 0 to {@code size}.
683 *
684 * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist,
685 * it will be created with the requested {@code size}. Thus this method is
686 * useful for creating memory mapped files which do not yet exist.
687 *
688 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
689 *
690 * @param file the file to map
691 * @param mode the mode to use when mapping {@code file}
692 * @return a buffer reflecting {@code file}
693 * @throws IOException if an I/O error occurs
694 *
695 * @see FileChannel#map(MapMode, long, long)
696 * @since 2
697 */
698 public static MappedByteBuffer map(File file, MapMode mode, long size)
699 throws FileNotFoundException, IOException {
700 RandomAccessFile raf =
701 new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw");
702
703 boolean threw = true;
704 try {
705 MappedByteBuffer mbb = map(raf, mode, size);
706 threw = false;
707 return mbb;
708 } finally {
709 Closeables.close(raf, threw);
710 }
711 }
712
713 private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode,
714 long size) throws IOException {
715 FileChannel channel = raf.getChannel();
716
717 boolean threw = true;
718 try {
719 MappedByteBuffer mbb = channel.map(mode, 0, size);
720 threw = false;
721 return mbb;
722 } finally {
723 Closeables.close(channel, threw);
724 }
725 }
726 }