系列中的上一篇
当前教程
操作文件和目录
系列中的下一篇

系列中的上一篇: 访问文件系统

系列中的下一篇: 链接、符号和其他

操作文件和目录

 

检查文件或目录

您有一个代表文件或目录的 Path 实例,但该文件是否存在于文件系统中?它是否可读?可写?可执行?

验证文件或目录的存在

Path 类中的方法是语法性的,这意味着它们在 Path 实例上运行。但最终您必须访问文件系统以验证特定 Path 是否存在,或不存在。您可以使用 exists(Path, LinkOption...)notExists(Path, LinkOption...) 方法来实现。请注意,!Files.exists(path) 不等同于 Files.notExists(path)。当您测试文件是否存在时,可能出现三种结果

  • 文件被验证存在。
  • 文件被验证不存在。
  • 文件的狀態未知。当程序无法访问文件时,可能会出现此结果。

如果 exists()notExists() 都返回 false,则无法验证文件是否存在。

检查文件可访问性

要验证程序是否可以按需访问文件,您可以使用 isReadable(Path)isWritable(Path)isExecutable(Path) 方法。

以下代码片段验证了特定文件是否存在,以及程序是否具有执行该文件的能力。

Path file = ...;
boolean isRegularExecutableFile = Files.isRegularFile(file) &
Files.isReadable(file) & Files.isExecutable(file);

注意:一旦这些方法中的任何一个完成,就不能保证可以访问该文件。许多应用程序中常见的安全漏洞是在执行检查后访问文件。有关更多信息,请使用您喜欢的搜索引擎查找 TOCTTOU(发音为 TOCK-too)。

检查两个路径是否定位同一个文件

当您使用符号链接的文件系统时,可以使用两个不同的路径来定位同一个文件。 isSameFile(Path, Path) 方法比较两个路径以确定它们是否定位文件系统上的同一个文件。例如

Path p1 = ...;
Path p2 = ...;

if (Files.isSameFile(p1, p2)) {
    // Logic when the paths locate the same file
}

 

删除文件或目录

您可以删除文件、目录或链接。对于符号链接,链接将被删除,而不是链接的目标。对于目录,目录必须为空,否则删除将失败。

Files 类提供了两种删除方法。

delete(Path) 方法删除文件,如果删除失败则抛出异常。例如,如果文件不存在,则会抛出 NoSuchFileException。您可以捕获异常以确定删除失败的原因,如下所示

try {
    Files.delete(path);
} catch (NoSuchFileException x) {
    System.err.format("%s: no such" + " file or directory%n", path);
} catch (DirectoryNotEmptyException x) {
    System.err.format("%s not empty%n", path);
} catch (IOException x) {
    // File permission problems are caught here.
    System.err.println(x);
}

deleteIfExists(Path) 方法也会删除文件,但如果文件不存在,则不会抛出异常。当您有多个线程删除文件并且您不想仅仅因为一个线程先删除了文件就抛出异常时,静默失败很有用。

 

复制文件或目录

您可以使用 copy(Path, Path, CopyOption...) 方法复制文件或目录。如果目标文件存在,则复制将失败,除非指定了 REPLACE_EXISTING 选项。

可以复制目录。但是,目录内的文件不会被复制,因此即使原始目录包含文件,新目录也是空的。

复制符号链接时,将复制链接的目标。如果您想复制链接本身,而不是链接的内容,请指定 NOFOLLOW_LINKSREPLACE_EXISTING 选项。

此方法采用 varargs 参数。以下 StandardCopyOptionLinkOption 枚举受支持

  • REPLACE_EXISTING – 即使目标文件已存在,也会执行复制。如果目标是符号链接,则复制链接本身(而不是链接的目标)。如果目标是非空目录,则复制将失败,并出现 DirectoryNotEmptyException 异常。
  • COPY_ATTRIBUTES – 将与文件关联的文件属性复制到目标文件。支持的确切文件属性取决于文件系统和平台,但最后修改时间在所有平台上都受支持,并且会复制到目标文件。
  • NOFOLLOW_LINKS – 指示不应跟随符号链接。如果要复制的文件是符号链接,则复制链接(而不是链接的目标)。

如果您不熟悉枚举,请参阅枚举类型部分。

以下显示了如何使用复制方法

import static java.nio.file.StandardCopyOption.*;

Files.copy(source, target, REPLACE_EXISTING);

除了文件复制之外,Files 类还定义了可用于在文件和流之间复制的方法。 copy(InputStream, Path, CopyOptions...) 方法可用于将输入流中的所有字节复制到文件。 copy(Path, OutputStream) 方法可用于将文件中的所有字节复制到输出流。

 

移动文件或目录

您可以使用 move(Path, Path, CopyOption...) 方法移动文件或目录。如果目标文件存在,则移动将失败,除非指定了 REPLACE_EXISTING 选项。

使用 Varargs

一些 Files 方法在指定标志时接受任意数量的参数。例如,在以下方法签名中,CopyOption 参数后的省略号表示该方法接受可变数量的参数,或者通常称为varargs

Path Files.move(Path, Path, CopyOption...)

当方法接受 varargs 参数时,您可以传递一个逗号分隔的值列表或一个值数组 (CopyOption[])。

在以下示例中,该方法可以按以下方式调用

Path source = ...;
Path target = ...;
Files.move(source,
           target,
           REPLACE_EXISTING,
           ATOMIC_MOVE);

移动目录

空目录可以移动。如果目录不为空,则只有在不移动目录内容的情况下才能移动该目录时才允许移动。在 UNIX 系统上,将目录移动到同一分区通常包括重命名目录。在这种情况下,即使目录包含文件,此方法也能正常工作。

此方法采用可变参数参数 - 支持以下 StandardCopyOption 枚举

  • REPLACE_EXISTING - 即使目标文件已存在,也执行移动操作。如果目标是符号链接,则符号链接将被替换,但它指向的内容不会受到影响。
  • ATOMIC_MOVE - 将移动操作作为原子文件操作执行。如果文件系统不支持原子移动,则会抛出异常。使用 ATOMIC_MOVE,您可以将文件移动到目录中,并保证任何监视该目录的进程都访问完整的文件。

以下展示了如何使用 move 方法

import static java.nio.file.StandardCopyOption.*;

Files.move(source, target, REPLACE_EXISTING);

虽然您可以像所示的那样在单个目录上实现 move() 方法,但该方法最常与文件树递归机制一起使用。有关更多信息,请参阅遍历文件树部分。

 

原子操作

一些 Files 方法,例如 move(),可以在某些文件系统中以原子方式执行某些操作。

原子文件操作是指无法中断或“部分”执行的操作。要么整个操作执行,要么操作失败。当您有多个进程在文件系统的同一区域上操作时,这非常重要,您需要保证每个进程都访问完整的文件。

 

Files 类是“链接感知”的。每个 Files 方法要么检测到遇到符号链接时该怎么做,要么提供一个选项,使您能够在遇到符号链接时配置行为。有关如何在文件系统上处理链接的更多信息,您可以查看 链接、符号和其他 部分。


最后更新: 2023 年 1 月 25 日


系列中的上一篇
当前教程
操作文件和目录
系列中的下一篇

系列中的上一篇: 访问文件系统

系列中的下一篇: 链接、符号和其他