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