All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.hasor.cobble.io.FileUtils Maven / Gradle / Ivy

/*
 * Copyright 2008-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.hasor.cobble.io;
import net.hasor.cobble.io.reader.ReversedLinesFileReader;

import java.io.*;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * General file manipulation utilities.
 * 

* Facilities are provided in the following areas: *

    *
  • writing to a file *
  • reading from a file *
  • make a directory including parent directories *
  • copying files and directories *
  • deleting files and directories *
  • converting to and from a URL *
  • listing files and directories by filter and extension *
  • comparing file content *
  • file last changed date *
  • calculating a checksum *
*

* Origin of code: Excalibur, Alexandria, Commons-Utils * * 2017-09-13 reduction based on Apache Commons IO, org.apache.commons.io.FileUtils * * @author 赵永春 ([email protected]) * @version $Id: FileUtils.java 1349509 2012-06-12 20:39:23Z ggregory $ */ public class FileUtils { /** * Reads the contents of a file into a String. * The file is always closed. * * @param file the file to read, must not be null * @param encoding the encoding to use, null means platform default * @return the file contents, never null * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM */ public static String readFileToString(File file, String encoding) throws IOException { InputStream in = null; try { in = openInputStream(file); return IOUtils.toString(in, encoding); } finally { IOUtils.closeQuietly(in); } } /** * Reads the contents of a file into a String using the default encoding for the VM. * The file is always closed. * * @param file the file to read, must not be null * @return the file contents, never null * @throws IOException in case of an I/O error * @since Commons IO 1.3.1 */ public static String readFileToString(File file) throws IOException { return readFileToString(file, null); } /** * Reads the contents of a file into a byte array. * The file is always closed. * * @param file the file to read, must not be null * @return the file contents, never null * @throws IOException in case of an I/O error * @since Commons IO 1.1 */ public static byte[] readFileToByteArray(File file) throws IOException { InputStream in = null; try { in = openInputStream(file); return IOUtils.toByteArray(in, file.length()); } finally { IOUtils.closeQuietly(in); } } /** * Reads the contents of a file line by line to a List of Strings. * The file is always closed. * * @param file the file to read, must not be null * @param encoding the encoding to use, null means platform default * @return the list of Strings representing each line in the file, never null * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 1.1 */ public static List readLines(File file, String encoding) throws IOException { InputStream in = null; try { in = openInputStream(file); return IOUtils.readLines(in, encoding); } finally { IOUtils.closeQuietly(in); } } /** * Reads the contents of a file line by line to a List of Strings using the default encoding for the VM. * The file is always closed. * * @param file the file to read, must not be null * @return the list of Strings representing each line in the file, never null * @throws IOException in case of an I/O error * @since Commons IO 1.3 */ public static List readLines(File file) throws IOException { return readLines(file, null); } /** * Reads the contents of a file from back to front of Strings using the default encoding for the VM. * The file is always closed. * * @param file the file to read, must not be null * @return the list of Strings representing each line in the file, never null * @throws IOException in case of an I/O error */ public static List reversedReadLines(File file, int maxLines) throws IOException { try (ReversedLinesFileReader fileReader = new ReversedLinesFileReader(file, StandardCharsets.UTF_8)) { int count = 0; List tailStrList = new ArrayList<>(); String line; while (true) { if ((line = fileReader.readLine()) == null) { break; } tailStrList.add(line); if (count >= maxLines) { break; } count++; } return tailStrList; } } /** * Returns an Iterator for the lines in a File. *

* This method opens an InputStream for the file. * When you have finished with the iterator you should close the stream * to free internal resources. This can be done by calling the * {@link LineIterator#close()} or * {@link LineIterator#closeQuietly(LineIterator)} method. *

* The recommended usage pattern is: *

     * LineIterator it = FileUtils.lineIterator(file, "UTF-8");
     * try {
     *   while (it.hasNext()) {
     *     String line = it.nextLine();
     *     /// do something with line
     *   }
     * } finally {
     *   LineIterator.closeQuietly(iterator);
     * }
     * 
*

* If an exception occurs during the creation of the iterator, the * underlying stream is closed. * * @param file the file to open for input, must not be null * @param encoding the encoding to use, null means platform default * @return an Iterator of the lines in the file, never null * @throws IOException in case of an I/O error (file closed) * @since Commons IO 1.2 */ public static LineIterator lineIterator(File file, String encoding) throws IOException { InputStream in = null; try { in = openInputStream(file); return IOUtils.lineIterator(in, encoding); } catch (IOException | RuntimeException ex) { IOUtils.closeQuietly(in); throw ex; } } /** * Returns an Iterator for the lines in a File using the default encoding for the VM. * * @param file the file to open for input, must not be null * @return an Iterator of the lines in the file, never null * @throws IOException in case of an I/O error (file closed) * @since Commons IO 1.3 * @see #lineIterator(File, String) */ public static LineIterator lineIterator(File file) throws IOException { return lineIterator(file, null); } //----------------------------------------------------------------------- /** * Writes a String to a file creating the file if it does not exist. * * NOTE: As from v1.3, the parent directories of the file will be created * if they do not exist. * * @param file the file to write * @param data the content to write to the file * @param encoding the encoding to use, null means platform default * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM */ public static void writeStringToFile(File file, String data, String encoding) throws IOException { writeStringToFile(file, data, encoding, false); } /** * Writes a String to a file creating the file if it does not exist. * * @param file the file to write * @param data the content to write to the file * @param encoding the encoding to use, null means platform default * @param append if true, then the String will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 2.1 */ public static void writeStringToFile(File file, String data, String encoding, boolean append) throws IOException { OutputStream out = null; try { out = openOutputStream(file, append); IOUtils.write(data, out, encoding); } finally { IOUtils.closeQuietly(out); } } /** * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. * * @param file the file to write * @param data the content to write to the file * @throws IOException in case of an I/O error */ public static void writeStringToFile(File file, String data) throws IOException { writeStringToFile(file, data, null, false); } /** * Writes a String to a file creating the file if it does not exist using the default encoding for the VM. * * @param file the file to write * @param data the content to write to the file * @param append if true, then the String will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @since Commons IO 2.1 */ public static void writeStringToFile(File file, String data, boolean append) throws IOException { writeStringToFile(file, data, null, append); } /** * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. * * @param file the file to write * @param data the content to write to the file * @throws IOException in case of an I/O error * @since Commons IO 2.0 */ public static void write(File file, CharSequence data) throws IOException { write(file, data, null, false); } /** * Writes a CharSequence to a file creating the file if it does not exist using the default encoding for the VM. * * @param file the file to write * @param data the content to write to the file * @param append if true, then the data will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @since Commons IO 2.1 */ public static void write(File file, CharSequence data, boolean append) throws IOException { write(file, data, null, append); } /** * Writes a CharSequence to a file creating the file if it does not exist. * * @param file the file to write * @param data the content to write to the file * @param encoding the encoding to use, null means platform default * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 2.0 */ public static void write(File file, CharSequence data, String encoding) throws IOException { write(file, data, encoding, false); } /** * Writes a CharSequence to a file creating the file if it does not exist. * * @param file the file to write * @param data the content to write to the file * @param encoding the encoding to use, null means platform default * @param append if true, then the data will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since IO 2.1 */ public static void write(File file, CharSequence data, String encoding, boolean append) throws IOException { String str = data == null ? null : data.toString(); writeStringToFile(file, str, encoding, append); } /** * Writes a byte array to a file creating the file if it does not exist. *

* NOTE: As from v1.3, the parent directories of the file will be created * if they do not exist. * * @param file the file to write to * @param data the content to write to the file * @throws IOException in case of an I/O error * @since Commons IO 1.1 */ public static void writeByteArrayToFile(File file, byte[] data) throws IOException { writeByteArrayToFile(file, data, false); } /** * Writes a byte array to a file creating the file if it does not exist. * * @param file the file to write to * @param data the content to write to the file * @param append if true, then bytes will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @since IO 2.1 */ public static void writeByteArrayToFile(File file, byte[] data, boolean append) throws IOException { OutputStream out = null; try { out = openOutputStream(file, append); out.write(data); } finally { IOUtils.closeQuietly(out); } } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The specified character encoding and the default line ending will be used. *

* NOTE: As from v1.3, the parent directories of the file will be created * if they do not exist. * * @param file the file to write to * @param encoding the encoding to use, null means platform default * @param lines the lines to write, null entries produce blank lines * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 1.1 */ public static void writeLines(File file, String encoding, Collection lines) throws IOException { writeLines(file, encoding, lines, null, false); } /** * Writes the toString() value of each item in a collection to * the specified File line by line, optionally appending. * The specified character encoding and the default line ending will be used. * * @param file the file to write to * @param encoding the encoding to use, null means platform default * @param lines the lines to write, null entries produce blank lines * @param append if true, then the lines will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 2.1 */ public static void writeLines(File file, String encoding, Collection lines, boolean append) throws IOException { writeLines(file, encoding, lines, null, append); } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The default VM encoding and the default line ending will be used. * * @param file the file to write to * @param lines the lines to write, null entries produce blank lines * @throws IOException in case of an I/O error * @since Commons IO 1.3 */ public static void writeLines(File file, Collection lines) throws IOException { writeLines(file, null, lines, null, false); } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The default VM encoding and the default line ending will be used. * * @param file the file to write to * @param lines the lines to write, null entries produce blank lines * @param append if true, then the lines will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @since Commons IO 2.1 */ public static void writeLines(File file, Collection lines, boolean append) throws IOException { writeLines(file, null, lines, null, append); } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The specified character encoding and the line ending will be used. *

* NOTE: As from v1.3, the parent directories of the file will be created * if they do not exist. * * @param file the file to write to * @param encoding the encoding to use, null means platform default * @param lines the lines to write, null entries produce blank lines * @param lineEnding the line separator to use, null is system default * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 1.1 */ public static void writeLines(File file, String encoding, Collection lines, String lineEnding) throws IOException { writeLines(file, encoding, lines, lineEnding, false); } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The specified character encoding and the line ending will be used. * * @param file the file to write to * @param encoding the encoding to use, null means platform default * @param lines the lines to write, null entries produce blank lines * @param lineEnding the line separator to use, null is system default * @param append if true, then the lines will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @throws UnsupportedEncodingException if the encoding is not supported by the VM * @since Commons IO 2.1 */ public static void writeLines(File file, String encoding, Collection lines, String lineEnding, boolean append) throws IOException { OutputStream out = null; try { out = openOutputStream(file, append); IOUtils.writeLines(lines, lineEnding, out, encoding); } finally { IOUtils.closeQuietly(out); } } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The default VM encoding and the specified line ending will be used. * * @param file the file to write to * @param lines the lines to write, null entries produce blank lines * @param lineEnding the line separator to use, null is system default * @throws IOException in case of an I/O error * @since Commons IO 1.3 */ public static void writeLines(File file, Collection lines, String lineEnding) throws IOException { writeLines(file, null, lines, lineEnding, false); } /** * Writes the toString() value of each item in a collection to * the specified File line by line. * The default VM encoding and the specified line ending will be used. * * @param file the file to write to * @param lines the lines to write, null entries produce blank lines * @param lineEnding the line separator to use, null is system default * @param append if true, then the lines will be added to the * end of the file rather than overwriting * @throws IOException in case of an I/O error * @since Commons IO 2.1 */ public static void writeLines(File file, Collection lines, String lineEnding, boolean append) throws IOException { writeLines(file, null, lines, lineEnding, append); } //----------------------------------------------------------------------- /** * Opens a {@link FileInputStream} for the specified file, providing better * error messages than simply calling new FileInputStream(file). *

* At the end of the method either the stream will be successfully opened, * or an exception will have been thrown. *

* An exception is thrown if the file does not exist. * An exception is thrown if the file object exists but is a directory. * An exception is thrown if the file exists but cannot be read. * * @param file the file to open for input, must not be null * @return a new {@link FileInputStream} for the specified file * @throws FileNotFoundException if the file does not exist * @throws IOException if the file object is a directory * @throws IOException if the file cannot be read * @since Commons IO 1.3 */ public static FileInputStream openInputStream(File file) throws IOException { if (file.exists()) { if (file.isDirectory()) { throw new IOException("File '" + file + "' exists but is a directory"); } if (file.canRead() == false) { throw new IOException("File '" + file + "' cannot be read"); } } else { throw new FileNotFoundException("File '" + file + "' does not exist"); } return new FileInputStream(file); } //----------------------------------------------------------------------- /** * Opens a {@link FileOutputStream} for the specified file, checking and * creating the parent directory if it does not exist. *

* At the end of the method either the stream will be successfully opened, * or an exception will have been thrown. *

* The parent directory will be created if it does not exist. * The file will be created if it does not exist. * An exception is thrown if the file object exists but is a directory. * An exception is thrown if the file exists but cannot be written to. * An exception is thrown if the parent directory cannot be created. * * @param file the file to open for output, must not be null * @return a new {@link FileOutputStream} for the specified file * @throws IOException if the file object is a directory * @throws IOException if the file cannot be written to * @throws IOException if a parent directory needs creating but that fails * @since Commons IO 1.3 */ public static FileOutputStream openOutputStream(File file) throws IOException { return openOutputStream(file, false); } /** * Opens a {@link FileOutputStream} for the specified file, checking and * creating the parent directory if it does not exist. *

* At the end of the method either the stream will be successfully opened, * or an exception will have been thrown. *

* The parent directory will be created if it does not exist. * The file will be created if it does not exist. * An exception is thrown if the file object exists but is a directory. * An exception is thrown if the file exists but cannot be written to. * An exception is thrown if the parent directory cannot be created. * * @param file the file to open for output, must not be null * @param append if true, then bytes will be added to the * end of the file rather than overwriting * @return a new {@link FileOutputStream} for the specified file * @throws IOException if the file object is a directory * @throws IOException if the file cannot be written to * @throws IOException if a parent directory needs creating but that fails * @since Commons IO 2.1 */ public static FileOutputStream openOutputStream(File file, boolean append) throws IOException { if (file.exists()) { if (file.isDirectory()) { throw new IOException("File '" + file + "' exists but is a directory"); } if (file.canWrite() == false) { throw new IOException("File '" + file + "' cannot be written to"); } } else { File parent = file.getParentFile(); if (parent != null) { if (!parent.mkdirs() && !parent.isDirectory()) { throw new IOException("Directory '" + parent + "' could not be created"); } } } return new FileOutputStream(file, append); } //----------------------------------------------------------------------- /** * Deletes a directory recursively. * * @param directory directory to delete * @throws IOException in case deletion is unsuccessful */ public static void deleteDirectory(File directory) throws IOException { if (!directory.exists()) { return; } if (!isSymlink(directory)) { cleanDirectory(directory); } if (!directory.delete()) { String message = "Unable to delete directory " + directory + "."; throw new IOException(message); } } /** * Cleans a directory without deleting it. * * @param directory directory to clean * @throws IOException in case cleaning is unsuccessful */ public static void cleanDirectory(File directory) throws IOException { if (!directory.exists()) { String message = directory + " does not exist"; throw new IllegalArgumentException(message); } if (!directory.isDirectory()) { String message = directory + " is not a directory"; throw new IllegalArgumentException(message); } File[] files = directory.listFiles(); if (files == null) { // null if security restricted throw new IOException("Failed to list contents of " + directory); } IOException exception = null; for (File file : files) { try { forceDelete(file); } catch (IOException ioe) { exception = ioe; } } if (null != exception) { throw exception; } } /** * Deletes a file, never throwing an exception. If file is a directory, delete it and all sub-directories. *

* The difference between File.delete() and this method are: *

    *
  • A directory to be deleted does not have to be empty.
  • *
  • No exceptions are thrown when a file or directory cannot be deleted.
  • *
* * @param file file or directory to delete, can be {@code null} * @return {@code true} if the file or directory was deleted, otherwise * {@code false} * * @since 1.4 */ public static boolean deleteQuietly(File file) { if (file == null) { return false; } try { if (file.isDirectory()) { cleanDirectory(file); } } catch (Exception ignored) { } try { return file.delete(); } catch (Exception ignored) { return false; } } /** * Schedules a directory recursively for deletion on JVM exit. * * @param directory directory to delete, must not be {@code null} * @throws NullPointerException if the directory is {@code null} * @throws IOException in case deletion is unsuccessful */ private static void deleteDirectoryOnExit(File directory) throws IOException { if (!directory.exists()) { return; } directory.deleteOnExit(); if (!isSymlink(directory)) { cleanDirectoryOnExit(directory); } } /** * Cleans a directory without deleting it. * * @param directory directory to clean, must not be {@code null} * @throws NullPointerException if the directory is {@code null} * @throws IOException in case cleaning is unsuccessful */ private static void cleanDirectoryOnExit(File directory) throws IOException { if (!directory.exists()) { String message = directory + " does not exist"; throw new IllegalArgumentException(message); } if (!directory.isDirectory()) { String message = directory + " is not a directory"; throw new IllegalArgumentException(message); } File[] files = directory.listFiles(); if (files == null) { // null if security restricted throw new IOException("Failed to list contents of " + directory); } IOException exception = null; for (File file : files) { try { forceDeleteOnExit(file); } catch (IOException ioe) { exception = ioe; } } if (null != exception) { throw exception; } } /** * Determines whether the specified file is a Symbolic Link rather than an actual file. *

* Will not return true if there is a Symbolic Link anywhere in the path, * only if the specific file is. *

* Note: the current implementation always returns {@code false} if the system * is detected as Windows using {@link FilenameUtils#isSystemWindows()} * * @param file the file to check * @return true if the file is a Symbolic Link * @throws IOException if an IO error occurs while checking the file * @since 2.0 */ public static boolean isSymlink(File file) throws IOException { if (file == null) { throw new NullPointerException("File must not be null"); } if (FilenameUtils.isSystemWindows()) { return false; } File fileInCanonicalDir = null; if (file.getParent() == null) { fileInCanonicalDir = file; } else { File canonicalDir = file.getParentFile().getCanonicalFile(); fileInCanonicalDir = new File(canonicalDir, file.getName()); } return !fileInCanonicalDir.getCanonicalFile().equals(fileInCanonicalDir.getAbsoluteFile()); } /** * Deletes a file. If file is a directory, delete it and all sub-directories. *

* The difference between File.delete() and this method are: *

    *
  • A directory to be deleted does not have to be empty.
  • *
  • You get exceptions when a file or directory cannot be deleted. * (java.io.File methods returns a boolean)
  • *
* * @param file file or directory to delete, must not be {@code null} * @throws NullPointerException if the directory is {@code null} * @throws FileNotFoundException if the file was not found * @throws IOException in case deletion is unsuccessful */ public static void forceDelete(File file) throws IOException { if (file.isDirectory()) { deleteDirectory(file); } else { boolean filePresent = file.exists(); if (!file.delete()) { if (!filePresent) { throw new FileNotFoundException("File does not exist: " + file); } String message = "Unable to delete file: " + file; throw new IOException(message); } } } /** * Schedules a file to be deleted when JVM exits. * If file is directory delete it and all sub-directories. * * @param file file or directory to delete, must not be {@code null} * @throws NullPointerException if the file is {@code null} * @throws IOException in case deletion is unsuccessful */ public static void forceDeleteOnExit(File file) throws IOException { if (file.isDirectory()) { deleteDirectoryOnExit(file); } else { file.deleteOnExit(); } } public static File toFile(URL file) { if (file == null) { return null; } try { return new File(file.toURI()); } catch (Exception e) { throw new RuntimeException(e); } } public static File toFile(URI file) { if (file == null) { return null; } try { return new File(file); } catch (Exception e) { throw new RuntimeException(e); } } /** 依据 baseDir 向下遍历目录并寻找符合 test 的结果 */ public static List walkDown(File baseDir, WalkFilter test) { return walk(baseDir, true, test); } /** 依据 baseDir 向上遍历目录并寻找符合 test 的结果 */ public static List walkUp(File baseDir, WalkFilter test) { return walk(baseDir, false, test); } /** 依据 baseDir 遍历目录并寻找符合 test 的结果,goDown 为 true 表示向下遍历,为 false 表示向上遍历 */ public static List walk(File baseDir, boolean goDown, WalkFilter filter) { if (baseDir == null) { return Collections.emptyList(); } ArrayList files = new ArrayList<>(); walkAppend(baseDir, goDown, filter, files, Long.MAX_VALUE); return files; } private static void walkAppend(File baseDir, boolean goDown, WalkFilter filter, List append, long depth) { if (baseDir == null || depth == 0) { return; } // current dir String[] lf = baseDir.list(); if (lf != null) { for (String f : lf) { File file = new File(baseDir, f); if ((filter == null) || filter.accept(file) || filter.accept(baseDir, f)) { append.add(file); if (goDown) { walkAppend(file, true, filter, append, depth--); } } } } // go up if (!goDown) { File parent = baseDir.getParentFile(); if (parent == null) { parent = baseDir.getAbsoluteFile().getParentFile(); } if (parent != null) { walkAppend(parent, false, filter, append, depth--); } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy