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.0
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.0
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 * <b>Deprecated.</b> This method suffers from poor symlink detection and race
493 * conditions. This functionality can be supported suitably only by shelling
494 * out to an operating system command such as {@code rm -rf} or {@code del
495 * /s}. This method is scheduled to be removed from Guava in Guava release
496 * 11.0.
497 *
498 * <p>Deletes all the files within a directory. Does not delete the
499 * directory itself.
500 *
501 * <p>If the file argument is a symbolic link or there is a symbolic
502 * link in the path leading to the directory, this method will do
503 * nothing. Symbolic links within the directory are not followed.
504 *
505 * @param directory the directory to delete the contents of
506 * @throws IllegalArgumentException if the argument is not a directory
507 * @throws IOException if an I/O error occurs
508 */
509 @Deprecated
510 public static void deleteDirectoryContents(File directory)
511 throws IOException {
512 Preconditions.checkArgument(directory.isDirectory(),
513 "Not a directory: %s", directory);
514 // Symbolic links will have different canonical and absolute paths
515 if (!directory.getCanonicalPath().equals(directory.getAbsolutePath())) {
516 return;
517 }
518 File[] files = directory.listFiles();
519 if (files == null) {
520 throw new IOException("Error listing files for " + directory);
521 }
522 for (File file : files) {
523 deleteRecursively(file);
524 }
525 }
526
527 /**
528 * <b>Deprecated.</b> This method suffers from poor symlink detection and race
529 * conditions. This functionality can be supported suitably only by shelling
530 * out to an operating system command such as {@code rm -rf} or {@code del
531 * /s}. This method is scheduled to be removed from Guava in Guava release
532 * 11.0.
533 *
534 * <p>Deletes a file or directory and all contents recursively.
535 *
536 * <p>If the file argument is a symbolic link the link will be deleted
537 * but not the target of the link. If the argument is a directory,
538 * symbolic links within the directory will not be followed.
539 *
540 * @param file the file to delete
541 * @throws IOException if an I/O error occurs
542 */
543 @Deprecated
544 public static void deleteRecursively(File file) throws IOException {
545 if (file.isDirectory()) {
546 deleteDirectoryContents(file);
547 }
548 if (!file.delete()) {
549 throw new IOException("Failed to delete " + file);
550 }
551 }
552
553 /**
554 * Reads the first line from a file. The line does not include
555 * line-termination characters, but does include other leading and
556 * trailing whitespace.
557 *
558 * @param file the file to read from
559 * @param charset the character set used when writing the file
560 * @return the first line, or null if the file is empty
561 * @throws IOException if an I/O error occurs
562 */
563 public static String readFirstLine(File file, Charset charset)
564 throws IOException {
565 return CharStreams.readFirstLine(Files.newReaderSupplier(file, charset));
566 }
567
568 /**
569 * Reads all of the lines from a file. The lines do not include
570 * line-termination characters, but do include other leading and
571 * trailing whitespace.
572 *
573 * @param file the file to read from
574 * @param charset the character set used when writing the file
575 * @return a mutable {@link List} containing all the lines
576 * @throws IOException if an I/O error occurs
577 */
578 public static List<String> readLines(File file, Charset charset)
579 throws IOException {
580 return CharStreams.readLines(Files.newReaderSupplier(file, charset));
581 }
582
583 /**
584 * Streams lines from a {@link File}, stopping when our callback returns
585 * false, or we have read all of the lines.
586 *
587 * @param file the file to read from
588 * @param charset the character set used when writing the file
589 * @param callback the {@link LineProcessor} to use to handle the lines
590 * @return the output of processing the lines
591 * @throws IOException if an I/O error occurs
592 */
593 public static <T> T readLines(File file, Charset charset,
594 LineProcessor<T> callback) throws IOException {
595 return CharStreams.readLines(Files.newReaderSupplier(file, charset),
596 callback);
597 }
598
599 /**
600 * Process the bytes of a file.
601 *
602 * <p>(If this seems too complicated, maybe you're looking for
603 * {@link #toByteArray}.)
604 *
605 * @param file the file to read
606 * @param processor the object to which the bytes of the file are passed.
607 * @return the result of the byte processor
608 * @throws IOException if an I/O error occurs
609 */
610 public static <T> T readBytes(File file, ByteProcessor<T> processor)
611 throws IOException {
612 return ByteStreams.readBytes(newInputStreamSupplier(file), processor);
613 }
614
615 /**
616 * Computes and returns the checksum value for a file.
617 * The checksum object is reset when this method returns successfully.
618 *
619 * @param file the file to read
620 * @param checksum the checksum object
621 * @return the result of {@link Checksum#getValue} after updating the
622 * checksum object with all of the bytes in the file
623 * @throws IOException if an I/O error occurs
624 */
625 public static long getChecksum(File file, Checksum checksum)
626 throws IOException {
627 return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum);
628 }
629
630 /**
631 * Computes and returns the digest value for a file.
632 * The digest object is reset when this method returns successfully.
633 *
634 * @param file the file to read
635 * @param md the digest object
636 * @return the result of {@link MessageDigest#digest()} after updating the
637 * digest object with all of the bytes in this file
638 * @throws IOException if an I/O error occurs
639 */
640 public static byte[] getDigest(File file, MessageDigest md)
641 throws IOException {
642 return ByteStreams.getDigest(newInputStreamSupplier(file), md);
643 }
644
645 /**
646 * Fully maps a file read-only in to memory as per
647 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}.
648 *
649 * <p>Files are mapped from offset 0 to its length.
650 *
651 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
652 *
653 * @param file the file to map
654 * @return a read-only buffer reflecting {@code file}
655 * @throws FileNotFoundException if the {@code file} does not exist
656 * @throws IOException if an I/O error occurs
657 *
658 * @see FileChannel#map(MapMode, long, long)
659 * @since 2.0
660 */
661 public static MappedByteBuffer map(File file) throws IOException {
662 return map(file, MapMode.READ_ONLY);
663 }
664
665 /**
666 * Fully maps a file in to memory as per
667 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
668 * using the requested {@link MapMode}.
669 *
670 * <p>Files are mapped from offset 0 to its length.
671 *
672 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
673 *
674 * @param file the file to map
675 * @param mode the mode to use when mapping {@code file}
676 * @return a buffer reflecting {@code file}
677 * @throws FileNotFoundException if the {@code file} does not exist
678 * @throws IOException if an I/O error occurs
679 *
680 * @see FileChannel#map(MapMode, long, long)
681 * @since 2.0
682 */
683 public static MappedByteBuffer map(File file, MapMode mode)
684 throws IOException {
685 if (!file.exists()) {
686 throw new FileNotFoundException(file.toString());
687 }
688 return map(file, mode, file.length());
689 }
690
691 /**
692 * Maps a file in to memory as per
693 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}
694 * using the requested {@link MapMode}.
695 *
696 * <p>Files are mapped from offset 0 to {@code size}.
697 *
698 * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist,
699 * it will be created with the requested {@code size}. Thus this method is
700 * useful for creating memory mapped files which do not yet exist.
701 *
702 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes.
703 *
704 * @param file the file to map
705 * @param mode the mode to use when mapping {@code file}
706 * @return a buffer reflecting {@code file}
707 * @throws IOException if an I/O error occurs
708 *
709 * @see FileChannel#map(MapMode, long, long)
710 * @since 2.0
711 */
712 public static MappedByteBuffer map(File file, MapMode mode, long size)
713 throws FileNotFoundException, IOException {
714 RandomAccessFile raf =
715 new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw");
716
717 boolean threw = true;
718 try {
719 MappedByteBuffer mbb = map(raf, mode, size);
720 threw = false;
721 return mbb;
722 } finally {
723 Closeables.close(raf, threw);
724 }
725 }
726
727 private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode,
728 long size) throws IOException {
729 FileChannel channel = raf.getChannel();
730
731 boolean threw = true;
732 try {
733 MappedByteBuffer mbb = channel.map(mode, 0, size);
734 threw = false;
735 return mbb;
736 } finally {
737 Closeables.close(channel, threw);
738 }
739 }
740
741 private static boolean sep(char[] a, int pos) {
742 return (pos >= a.length) || (a[pos] == '/');
743 }
744
745 }