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 }