Java NIO文件

时间:2020-01-09 14:20:01  来源:igfitidea点击:

Java NIO Files类(java.nio.file.Files)提供了几种在文件系统中操作文件的方法。本Java NIO"文件"教程将介绍这些方法中最常用的方法。 Files类包含许多方法,因此,如果需要此处未描述的方法,也请检查JavaDoc。 Files类仍然可能有一个方法。

" java.nio.file.Files"类可与" java.nio.file.Path"实例一起使用,因此在使用" Files"类之前,我们需要了解" Path"类。

Files.exists()

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

可以创建文件系统中不存在的Path实例。例如,如果我们打算创建一个新目录,则首先要创建相应的Path实例,然后再创建目录。

由于Path实例可能指向或者不指向文件系统中存在的路径,因此我们可以使用Files.exists()方法确定它们是否存在(以防我们需要检查)。

这是一个JavaFiles.exists()示例:

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

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

这个例子首先创建一个Path实例,该实例指向我们要检查的路径是否存在。其次,该示例以" Path"实例作为第一个参数调用" Files.exists()"方法。

注意Files.exists()方法的第二个参数。这个参数是一系列选项,这些选项会影响Files.exists()确定路径是否存在的方式。在上面的示例中,数组包含" LinkOption.NOFOLLOW_LINKS",这意味着" Files.exists()"方法不应跟随文件系统中的符号链接来确定路径是否存在。

Files.createDirectory()

Files.createDirectory()方法从Path实例创建一个新目录。这是一个JavaFiles.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块中,使用路径作为参数调用Files.createDirectory()方法。如果创建目录成功,则将返回一个Path实例,该实例指向新创建的路径。

如果目录已经存在,将抛出java.nio.file.FileAlreadyExistsException。如果其他地方出了问题,可能会抛出IOException。例如,如果所需的新目录的父目录不存在,则可能抛出" IOException"。父目录是我们要其中创建新目录的目录。因此,这意味着新目录的父目录。

Files.copy()

Files.copy()方法将文件从一个路径复制到另一个路径。这是一个Java NIOFiles.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(),并将两个Path实例作为参数传递。这将导致源路径引用的文件被复制到目标路径引用的文件。

如果目标文件已经存在,则抛出" java.nio.file.FileAlreadyExistsException"。如果其他地方出了问题,将抛出" IOException"。例如,如果将文件复制到的目录不存在,则将抛出" IOException"。

覆盖现有文件

可以强制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()方法的第三个参数。如果目标文件已经存在,则此参数指示copy()方法覆盖现有文件。

Files.move()

Java NIOFiles类还包含一个用于将文件从一个路径移动到另一路径的函数。移动文件与重命名相同,只是移动文件既可以将文件移动到另一个目录,又可以在同一操作中更改其名称。是的,java.io.File类也可以通过其renameTo()方法来做到这一点,但是现在我们在java.nio.file.Files类中也具有文件移动函数。

这是一个JavaFiles.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()方法可以删除文件或者目录。这是一个JavaFiles.delete()示例:

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

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

首先创建指向要删除文件的Path。其次,调用Files.delete()方法。如果Files.delete()由于某种原因未能删除文件(例如,文件或者目录不存在),则抛出IOException

Files.walkFileTree()

Files.walkFileTree()方法包含用于递归遍历目录树的函数。 walkFileTree()方法采用Path实例和FileVisitor作为参数。 " Path"实例指向我们要遍历的目录。在遍历期间将调用FileVisitor。

在解释遍历如何工作之前,首先是" 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

通过返回这些值之一,被调用的方法可以决定如何继续文件遍历。

" CONTINUE"表示文件继续浏览应照常进行。

TERMINATE表示文件遍历应立即终止。

" SKIP_SIBLINGS"表示应该继续进行文件遍历,但不要访问该文件或者目录的任何同级文件。

" SKIP_SUBTREE"表示文件继续浏览,但不访问该目录中的条目。该值仅从preVisitDirectory()返回时才具有函数。如果从任何其他方法返回,它将被解释为" CONTINUE"。

搜索文件

这是一个walkFileTree(),它扩展了'SimpleFileVisitor来查找名为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();
}

递归删除目录

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类中的其他方法

" java.nio.file.Files"类包含许多其他有用的函数,例如用于创建符号链接,确定文件大小,设置文件许可权的函数。