Java NIO 中的 Files 类(java.nio.file.Files)提供了多种在文件系统中操作文件的方法。本节教程将覆盖最常用的方法。Files 类包含了许多方法,所以如果本文没有提到的你也可以直接查询 JavaDoc 文档。

阅读文章的过程中如果有任何疑问,欢迎添加笔者为好友,拉您进【七日书摘】微信交流群,一起交流技术,一起打造高质量的职场技术交流圈子,抱团取暖,共同进步。
七日书摘官方群.jpg

java.nio.file.Files 类是和 java.nio.file.Path 相结合使用的,所以在用 Files 之前确保你已经理解了 Path 类。

Files.exists()

Files.exits() 方法用来检查给定的 Path 在文件系统中是否存在。

在文件系统中你可以创建一个原本不存在的 Path 实例。例如,你要新建一个目录,那么首先创建对应的 Path 实例,然后再创建目录。

由于 Path 实例可能指向文件系统中不存在的路径,因此你需要使用 Files.exists() 方法来确认。

下面是一个使用 Files.exists() 的示例:

Path path = Paths.get("data/logging.properties");

boolean pathExists = Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});

在该示例中,首先创建了一个 Path 对象,然后利用 Files.exists() 来检查这个 Path 实例是否真实存在。

注意 Files.exists() 的的第二个参数。它是一个数组,这个参数直接影响 Files.exists() 如何确定一个路径是否存在。在该示例中,数组内包含了 LinkOptions.NOFOLLOW_LINKS,表示 Files.exists() 检测时不包含符号链接文件。

创建目录(Files.createDirectory())

Files.createDirectory() 方法的作用是从 Path 实例创建一个新目录。
下面是一个使用 Files.createDirectory() 示例:

Path path = Paths.get("data/subdir");

try {
    Path newDir = Files.createDirectory(path);
} catch(FileAlreadyExistsException e){
    // the directory already exists.
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}

第一行表示需要创建目录的 Path 实例。接着在 try-catch 块中使用 Path 作为 Files.createDirectory() 方法的参数进行创建,并进行异常捕获。如果目录创建成功,那么返回值就是新创建的路径。

如果目录已经存在,那么会抛出 java.nio.file.FileAlreadyExistException 异常。如果出现其他问题则会抛出 IOException。例如,要创建的目录的父目录不存在,那么就会抛出 IOException。父目录指的是你要创建的目录所在的位置,也就是新创建的目录的上一级目录。

文件拷贝(Files.copy())

Files.copy() 方法的作用可以把一个文件从一个路径复制到另一个路径。
下面是一个 Files.copy() 的使用示例:

Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");

try {
    Files.copy(sourcePath, destinationPath);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}

在该示例中,首先创建原文件和目标文件的 Path 实例。然后把它们作为参数传递给 Files.copy(),接着就会实现把文件从源文件路径拷贝到目标路径下。

如果目标文件已经存在,则会抛出 java.nio.file.FileAlreadyExistsException 异常。类似的如果发生其他错误,将抛出 IOException。例如,如果要将文件复制到的目录不存在,将引发IOException。

覆盖已经存在的文件(Overwriting Existing Files)

Files.copy()方法可以强制覆盖已经存在的文件。
下面是一个使用 Files.copy()的示例:

Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");

try {
    Files.copy(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}

注意 Files.copy() 方法的第三个可选参数,这个参数决定是否覆盖已存在的文件。

移动文件(Files.move())

Java NIO 的 Files 类还包含了一个用于将文件从一个路径移动到另一个路径的函数。移动文件和重命名是一样的,但是移动文件除了可以更改其名称外还可以改变文件的目录位置。java.io.File 类中的 renameTo() 方法可更改其名称。

下面是一个 Files.move() 示例:

Path sourcePath      = Paths.get("data/logging-copy.properties");
Path destinationPath = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.move(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    //moving file failed.
    e.printStackTrace();
}

首先创建原路径和目标路径,原路径指向需要移动的文件,而目标路径指向应将文件移动到的位置,然后调用 Files.move() 方法进行文件移动。

注意传递给Files.move()的第三个可选参数,此参数告诉 Files.move()方法覆盖目标路径下的相应文件。

如果移动文件失败,Files.move()方法可能会引发 IOException。例如,如果目标路径中已经存在一个文件,并且你忽略了第三个参数 StandardCopyOption.REPLACE_EXISTING选项,或者移动的文件不存在等等。

删除文件(Files.delete())

Files.delete()方法用于删除一个文件或目录。下面是一个 Files.delete() 使用示例:

Path path = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.delete(path);
} catch (IOException e) {
    //deleting file failed
    e.printStackTrace();
}

首先创建指向要删除的文件的路径,然后调用 Files.delete() 方法。如果 Files.delete() 由于某种原因(例如:文件或目录不存在)未能删除文件,则会引发 IOException

Files.walkFileTree()

Files.walkFileTree() 方法包含递归遍历目录树的功能。Files.walkFileTree()接受一个 PathFileVisitor 作为参数。Path 对象指向需要遍历的目录,FileVistor 则会在每次遍历中被调用。

在解释遍历是如何工作的之前,首先先看下面 FileVisitor 接口的定义示例:

public interface FileVisitor {

    public FileVisitResult preVisitDirectory(
        Path dir, BasicFileAttributes attrs) throws IOException;

    public FileVisitResult visitFile(
        Path file, BasicFileAttributes attrs) throws IOException;

    public FileVisitResult visitFileFailed(
        Path file, IOException exc) throws IOException;

    public FileVisitResult postVisitDirectory(
        Path dir, IOException exc) throws IOException {

}

FileVisitor 接口需要调用方自行实现,然后将实现的实例作为参数传入 walkFileTree()方法。FileVisitor的每个方法会在遍历过程中的不同时期被调用。如果不需要处理每个方法,那么可以继承他的默认实现类 SimpleFileVisitor,该类包含了 FileVisitor 接口中所有方法的默认实现。
下面是 walkFileTree() 示例:

Files.walkFileTree(path, new FileVisitor<Path>() {
  @Override
  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
    System.out.println("pre visit dir:" + dir);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    System.out.println("visit file: " + file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
    System.out.println("visit file failed: " + file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    System.out.println("post visit directory: " + dir);
    return FileVisitResult.CONTINUE;
  }
});

在遍历过程中,FileVisitor 的方法会在的不同时间被调用:

preVisitDirectory() 方法是在访问任何目录之前被调用。postVisitDirectory() 方法是在访问目录之后后被调用的。

visitFile() 会在整个遍历过程中的每次访问文件时被调用。该方法不是针对目录的,而是针对文件的。visitFileFailed() 方法则是在文件访问失败的时候被调用。例如,当无访问权限或者发生了其他错误。

上述四个方法都返回一个 FileVisitResult 枚举对象。FileVisitResult 枚举包含以下四个选项:

  • CONTINUE
  • TERMINATE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE

通过返回其中一个枚举值,可以让调用方决定如何继续文件遍。

CONTINE 表示文件遍历应继续正常进行。

TERMINATE 表示文件遍历现在需要终止。

SKIP_SIBLINGS 表示文件遍历应继续,但不要访问其同级文件或任何同级目录。

SKIP_SUBTREE 表示文件遍历应该继续,但不要访问该目录下的子目录。这个枚举值仅在 preVisitDirectory() 中返回才有效。如果在另外几个方法中返回,那么会被理解为 CONTINE。

搜索文件(Searching For Files)

下面是一个 walkFileTree() 例子,通过 walkFileTree() 来寻找一个README.txt文件:

Path rootPath = Paths.get("data");
String fileToFind = File.separator + "README.txt";

try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      String fileString = file.toAbsolutePath().toString();
      //System.out.println("pathString = " + fileString);

      if(fileString.endsWith(fileToFind)){
        System.out.println("file found at path: " + file.toAbsolutePath());
        return FileVisitResult.TERMINATE;
      }
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
    e.printStackTrace();
}

Deleting Directories Recursively

Files.walkFileTree() 方法也可以用来删除一个目录以及目录以下的所有文件和子目录。Files.delete() 方法仅用于删除一个空目录。通过遍历所有目录然后在 visitFile() 接口中删除每个目录中的所有文件,最后在 postVisitDirectory() 内删除目录本身。

下面是一个删除遍历目录示例:

Path rootPath = Paths.get("data/to-delete");

try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      System.out.println("delete file: " + file.toString());
      Files.delete(file);
      return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      Files.delete(dir);
      System.out.println("delete dir: " + dir.toString());
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
  e.printStackTrace();
}

Files类中的其他方法(Additional Methods in the Files Class)

java.nio.file.Files 类还包含血多其它有用的方法,例如用于创建符号链接、确定文件大小以及设置文件权限的方法等。具体用法可以查阅 java.nio.file.Files 类的JavaDoc 说明。

英文原文链接:http://tutorials.jenkov.com/java-nio/files.html

------完------

推荐阅读:

Java NIO 简明教程 之 Java NIO 非阻塞式服务器(Non-blocking Server)

Java NIO 简明教程 之 Java NIO 数据报通道(DatagramChannel)

Java NIO 简明教程 之 Java NIO 管道(Pipe)

Java NIO 简明教程 之 Java NIO vs. IO

Java NIO 简明教程 之 路径(Path)

Java基础知识面试题篇(2020年2月最新版)

更技术学习请进入七日书摘官方群: 七日书摘官方群

七日书摘官方群群聊二维码.png

参考资源:
http://tutorials.jenkov.com/java-nio/files.html
https://wiki.jikexueyuan.com/project/java-nio-zh/java-nio-files.html