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