系列中的上一篇
当前教程
管理文件属性
系列中的下一篇

系列中的上一篇: 链接、符号等

系列中的下一篇: 创建和读取目录

管理文件属性

 

文件和文件存储属性

文件系统的元数据通常称为其文件属性。 Files 类包含可用于获取文件单个属性或设置属性的方法。

方法 注释
size(Path) 以字节为单位返回指定文件的大小。
isDirectory(Path, LinkOption) 如果指定的 Path 定位到一个目录文件,则返回 true。
isRegularFile(Path, LinkOption...) 如果指定的 Path 定位到一个常规文件,则返回 true。
isSymbolicLink(Path) 如果指定的 Path 定位到一个符号链接文件,则返回 true。
isHidden(Path) 如果指定的 Path 定位到一个被文件系统视为隐藏的文件,则返回 true。
getLastModifiedTime(Path, LinkOption...) setLastModifiedTime(Path, FileTime) 返回或设置指定文件的最后修改时间。
getOwner(Path, LinkOption...) setOwner(Path, UserPrincipal) 返回或设置文件的拥有者。
getPosixFilePermissions(Path, LinkOption...) setPosixFilePermissions(Path, Set<PosixFilePermission>) 返回或设置文件的 POSIX 文件权限。
getAttribute(Path, String, LinkOption...) setAttribute(Path, String, Object, LinkOption...) 返回或设置文件属性的值。

如果程序需要在同一时间使用多个文件属性,则使用检索单个属性的方法可能会效率低下。反复访问文件系统以检索单个属性可能会对性能产生负面影响。为此,Files 类提供了两个 readAttributes() 方法,以便在一个批量操作中获取文件的属性。

方法 注释
readAttributes(Path, String, LinkOption...) 以批量操作方式读取文件的属性。 String 参数标识要读取的属性。
readAttributes(Path, Class<A>, LinkOption...) 以批量操作方式读取文件的属性。 Class<A> 参数是请求的属性类型,该方法返回该类的对象。

在展示 readAttributes() 方法的示例之前,应该提到不同的文件系统对应该跟踪哪些属性有不同的概念。为此,相关的文件属性被分组到视图中。视图映射到特定文件系统实现(例如 POSIX 或 DOS)或常见功能(例如文件所有权)。

支持的视图如下

  • BasicFileAttributeView – 提供对所有文件系统实现都必须支持的基本属性的视图。
  • DosFileAttributeView – 使用支持 DOS 属性的文件系统上支持的标准四位扩展基本属性视图。
  • PosixFileAttributeView – 使用支持 POSIX 标准系列(例如 UNIX)的文件系统上支持的属性扩展基本属性视图。这些属性包括文件所有者、组所有者以及九个相关的访问权限。
  • FileOwnerAttributeView – 支持任何支持文件所有者概念的文件系统实现。
  • AclFileAttributeView – 支持读取或更新文件的访问控制列表 (ACL)。支持 NFSv4 ACL 模型。任何具有到 NFSv4 模型的明确映射的 ACL 模型(例如 Windows ACL 模型)也可能得到支持。
  • UserDefinedFileAttributeView – 支持用户定义的元数据。此视图可以映射到系统支持的任何扩展机制。例如,在 Solaris 操作系统中,可以使用此视图存储文件的 MIME 类型。

特定文件系统实现可能只支持基本文件属性视图,或者可能支持这些文件属性视图中的几个。文件系统实现可能支持此 API 中未包含的其他属性视图。

在大多数情况下,您不必直接处理任何 FileAttributeView 接口。(如果您确实需要直接使用 FileAttributeView,您可以通过 getFileAttributeView(Path, Class<V>, LinkOption...) 方法访问它。)

readAttributes() 方法使用泛型,可用于读取任何文件属性视图的属性。本页其余部分中的示例使用 readAttributes() 方法。

 

基本文件属性

如前所述,要读取文件的基本属性,可以使用 Files.readAttributes() 方法之一,该方法在一个批量操作中读取所有基本属性。这比单独访问文件系统以读取每个单独属性要高效得多。varargs 参数当前支持 LinkOption 枚举,NOFOLLOW_LINKS。当您不希望跟踪符号链接时,请使用此选项。

关于时间戳的一句话:基本属性集包括三个时间戳:creationTimelastModifiedTimelastAccessTime。在特定实现中可能不支持这些时间戳中的任何一个,在这种情况下,相应的访问器方法将返回一个特定于实现的值。如果支持,则时间戳将作为 FileTime 对象返回。

以下代码片段读取并打印给定文件的基本文件属性,并使用 BasicFileAttributes 类中的方法。

Path file = ...;
BasicFileAttributes attr = Files.readAttributes(file, BasicFileAttributes.class);

System.out.println("creationTime: " + attr.creationTime());
System.out.println("lastAccessTime: " + attr.lastAccessTime());
System.out.println("lastModifiedTime: " + attr.lastModifiedTime());

System.out.println("isDirectory: " + attr.isDirectory());
System.out.println("isOther: " + attr.isOther());
System.out.println("isRegularFile: " + attr.isRegularFile());
System.out.println("isSymbolicLink: " + attr.isSymbolicLink());
System.out.println("size: " + attr.size());

除了本示例中显示的访问器方法之外,还有一个 fileKey() 方法,它返回一个唯一标识文件的对象,或者如果文件密钥不可用,则返回 null

设置时间戳

以下代码片段以毫秒为单位设置最后修改时间

Path file = ...;
BasicFileAttributes attr =
    Files.readAttributes(file, BasicFileAttributes.class);
long currentTime = System.currentTimeMillis();
FileTime ft = FileTime.fromMillis(currentTime);
Files.setLastModifiedTime(file, ft);

 

DOS 文件属性

DOS 文件属性也支持在除 DOS 之外的文件系统上,例如 Samba。以下代码片段使用 DosFileAttributes 类的方法。

Path file = ...;
try {
    DosFileAttributes attr =
        Files.readAttributes(file, DosFileAttributes.class);
    System.out.println("isReadOnly is " + attr.isReadOnly());
    System.out.println("isHidden is " + attr.isHidden());
    System.out.println("isArchive is " + attr.isArchive());
    System.out.println("isSystem is " + attr.isSystem());
} catch (UnsupportedOperationException x) {
    System.err.println("DOS file" +
        " attributes not supported:" + x);
}

但是,您可以使用 setAttribute(Path, String, Object, LinkOption...) 方法设置 DOS 属性,如下所示

Path file = ...;
Files.setAttribute(file, "dos:hidden", true);

 

POSIX 文件权限

POSIX 是可移植操作系统接口的缩写,用于 UNIX,是一组 IEEE 和 ISO 标准,旨在确保不同 UNIX 版本之间的互操作性。如果程序符合这些 POSIX 标准,则应该可以轻松地移植到其他符合 POSIX 标准的操作系统。

除了文件所有者和组所有者之外,POSIX 还支持九个文件权限:读取写入执行权限,分别适用于文件所有者、同一组的成员以及“其他所有人”。

以下代码片段读取给定文件的 POSIX 文件属性并将其打印到标准输出。该代码使用 PosixFileAttributes 类中的方法。

Path file = ...;
PosixFileAttributes attr =
    Files.readAttributes(file, PosixFileAttributes.class);
System.out.format("%s %s %s%n",
    attr.owner().getName(),
    attr.group().getName(),
    PosixFilePermissions.toString(attr.permissions()));

PosixFilePermissions 辅助类提供以下几种有用的方法

以下代码片段从一个文件读取属性,并创建一个新文件,将原始文件的属性分配给新文件

Path sourceFile = ...;
Path newFile = ...;
PosixFileAttributes attrs =
    Files.readAttributes(sourceFile, PosixFileAttributes.class);
FileAttribute<Set<PosixFilePermission>> attr =
    PosixFilePermissions.asFileAttribute(attrs.permissions());
Files.createFile(file, attr);

asFileAttribute() 方法将权限包装为 FileAttribute。然后,代码尝试使用这些权限创建一个新文件。请注意,umask 也会应用,因此新文件可能比请求的权限更安全。

要将文件的权限设置为以硬编码字符串表示的值,可以使用以下代码

Path file = ...;
Set<PosixFilePermission> perms =
    PosixFilePermissions.fromString("rw-------");
FileAttribute<Set<PosixFilePermission>> attr =
    PosixFilePermissions.asFileAttribute(perms);
Files.setPosixFilePermissions(file, perms);

 

设置文件或组所有者

要将名称转换为可以存储为文件所有者或组所有者的对象,可以使用 UserPrincipalLookupService 服务。此服务查找名称或组名作为字符串,并返回一个 UserPrincipal 对象,表示该字符串。您可以使用 FileSystem.getUserPrincipalLookupService() 方法获取默认文件系统的用户主体查找服务。

以下代码片段展示了如何使用 setOwner() 方法设置文件所有者

Path file = ...;
UserPrincipal owner = file.getFileSystem().getUserPrincipalLookupService()
        .lookupPrincipalByName("sally");
Files.setOwner(file, owner);

Files 类中没有专门用于设置组所有者的方法。但是,一种安全的方法是直接通过 POSIX 文件属性视图,如下所示

Path file = ...;
GroupPrincipal group =
    file.getFileSystem().getUserPrincipalLookupService()
        .lookupPrincipalByGroupName("green");
Files.getFileAttributeView(file, PosixFileAttributeView.class)
        .setGroup(group);

 

用户定义的文件属性

如果文件系统实现支持的文件属性不足以满足您的需求,您可以使用 UserDefinedAttributeView 创建和跟踪您自己的文件属性。

一些实现将此概念映射到诸如 NTFS 备用数据流和 ext3 和 ZFS 等文件系统上的扩展属性之类的功能。大多数实现对值的尺寸施加限制,例如,ext3 将尺寸限制为 4 千字节。

可以使用此代码片段将文件的 MIME 类型存储为用户定义的属性

Path file = ...;
UserDefinedFileAttributeView view =
    Files.getFileAttributeView(file, UserDefinedFileAttributeView.class);
view.write("user.mimetype",
           Charset.defaultCharset().encode("text/html");

要读取 MIME 类型属性,可以使用此代码片段

Path file = ...;
UserDefinedFileAttributeView view =
    Files.getFileAttributeView(file,UserDefinedFileAttributeView.class);
String name = "user.mimetype";
ByteBuffer buf = ByteBuffer.allocate(view.size(name));
view.read(name, buf);
buf.flip();
String value = Charset.defaultCharset().decode(buf).toString();

注意:在 Linux 中,您可能需要为用户定义的属性启用扩展属性才能使其正常工作。如果您在尝试访问用户定义的属性视图时收到 UnsupportedOperationException,则需要重新挂载文件系统。以下命令使用扩展属性重新挂载根分区以用于 ext3 文件系统。如果此命令不适用于您的 Linux 版本,请参阅文档。

$ sudo mount -o remount,user_xattr /

如果您想使更改永久生效,请在 /etc/fstab 中添加一个条目。

 

文件存储属性

您可以使用 FileStore 类来了解有关文件存储的信息,例如有多少空间可用。 getFileStore(Path) 方法获取指定文件的存储文件。

以下代码片段打印特定文件所在的存储文件的空间使用情况

Path file = ...;
FileStore store = Files.getFileStore(file);

long total = store.getTotalSpace() / 1024;
long used = (store.getTotalSpace() -
             store.getUnallocatedSpace()) / 1024;
long avail = store.getUsableSpace() / 1024;

 

确定 MIME 类型

要确定文件的 MIME 类型,您可能会发现 probeContentType(Path) 方法很有用。例如

try {
    String type = Files.probeContentType(filename);
    if (type == null) {
        System.err.format("'%s' has an" + " unknown filetype.%n", filename);
    } else if (!type.equals("text/plain") {
        System.err.format("'%s' is not" + " a plain text file.%n", filename);
        continue;
    }
} catch (IOException x) {
    System.err.println(x);
}

请注意,如果无法确定内容类型,此方法将返回 null。

此方法的实现高度依赖于平台,并非万无一失。内容类型由平台的默认文件类型检测器确定。例如,如果检测器根据 .class 扩展名确定文件的类型为 application/x-java,则可能会被欺骗。

如果默认类型不足以满足您的需求,您可以提供自定义的 FileTypeDetector


上次更新: 2023 年 1 月 25 日


系列中的上一篇
当前教程
管理文件属性
系列中的下一篇

系列中的上一篇: 链接、符号等

系列中的下一篇: 创建和读取目录