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 }