Java 7 中 NIO.2 的使用——第二节 元数据文件的属性

时间:2023-03-09 17:34:26
Java 7 中 NIO.2 的使用——第二节 元数据文件的属性

  如果你有很多疑问关于一个文件或目录,它是否是隐藏的,它的大小是多少,谁拥有它,你可以从元数据中得到这些信息。所谓的元数据,就是描述数据的数据。

  NIO.2组织了这些原数据的属性的概念,并提供了java.nio.file.attribute 包去访问它们。因为不同的文件系统有不同的概念跟踪这些属性,所以,NIO.2根据特定的文件系统进行映射然后对应到不同的view中。通常情况下,view会提供一些通用的方法,例如readAttributes()。除此而外,你可以通过getAttribute()setAttribute() 方法来提取或设置单一的属性,这些方法在 java.nio.file.Files 类中都是可用的。依据view,其他的方法可以完成额外的任务。

  在NIO.2 中支持六种view,下面就是这些view的概述。

  • BasicFileAttributeView:这是最基本的属性并且被所有的文件系统所支持。这个属性视图的名字叫做basic。
  • DosFileAttributeView:Windows操作系统中的文件系统一般使用遗留的“DOS”文件属性视图,该视图包含的属性有是否为只读文件(readonly)、是否为隐藏文件(hidden)、是否为系统文件(system)和是否为归档文件(archive)等。DosFileAttributeView接口中包含了设置这些属性的方法。
  • PosixFileAttributeView:这个视图是基本属性视图的扩展,用来支持可移植操作系统的Unix。
  • FileOwnerAttributeView:这个视图支持包含文件所有者之一概念的文件系统的实现。
  • AclFileAttributeView:这个视图支持读取和更新文件的ACL(access control list)操作。
  • UserDefinedFileAttributeView:这个视图支持用户自定义的元数据。

  --------------------------------------------------------------------------------------------------------------------------------

  确定视图支持一个特定的文件系统

  在你尝试访问view的属性时,请确认你的文件系统是否支持对应的view。NIO.2可以让你根据名字或是检查文件的存储(FileStore类描述了任何存储类型的映射,例如分区,设备,卷,等等)来列出所有支持的视图列表。

  一旦获取了默认系统的访问权限,通过调用 FileSystems.getDefault()方法,你就会很容易的遍历所有的支持的view通过调用 FileSystem.supportedFileAttributeViews()方法。请看下面的代码。

import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.util.Set;

FileSystem fs = FileSystems.getDefault();
Set<String> views = fs.supportedFileAttributeViews(); for (String view : views) {
System.out.println(view);
}

  在Windows7下执行的结果:

  owner
  dos
  acl
  basic
  user

  你可以通过调用FileStore.supportsFileAttributeView() 方法来测试特定的文件存储上的view,你可以传递view描述的字符串,或是直接传递view的接口名字。下面演示在支持的文件存储上检查是否支持basic视图的代码。

import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.attribute.BasicFileAttributeView;

FileSystem fs = FileSystems.getDefault();
for (FileStore store : fs.getFileStores()) {
boolean supported = store.supportsFileAttributeView(BasicFileAttributeView.class);
System.out.println(store.name() + " ---" + supported);
}

  当然,你也可以测试一个特定的文件支持的view。

import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt"); try {
FileStore store = Files.getFileStore(path);
boolean supported = store.supportsFileAttributeView("basic");
System.out.println(store.name() + " ---" + supported);
} catch (IOException e) {
System.err.println(e);
}

  --------------------------------------------------------------------------------------------------------------------------------

  Basic 视图

  大部分文件系统的实现支持一些通用的属性,例如大小,创建时间,上次访问时间,最后修改时间等等。这些属性被分组放在BasicFileAttributeView中,可以提取或设置下面描述的部分中。

  使用readAttributes()方法获得成批的属性

  你可以通过使用readAttributes()方法获取成批的属性,这个方法的参数支持 LinkOption.NOFOLLOW_LINKS 枚举类型,意思是不包含符号链接的文件。

 import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;

BasicFileAttributes attr = null;
Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt"); try {
attr = Files.readAttributes(path, BasicFileAttributes.class);
} catch (IOException e) {
System.err.println(e);
} System.out.println("File size: " + attr.size());
System.out.println("File creation time: " + attr.creationTime());
System.out.println("File was last accessed at: " + attr.lastAccessTime());
System.out.println("File was last modified at: " + attr.lastModifiedTime()); System.out.println("Is directory? " + attr.isDirectory());
System.out.println("Is regular file? " + attr.isRegularFile());
System.out.println("Is symbolic link? " + attr.isSymbolicLink());
System.out.println("Is other? " + attr.isOther());

  通过getAttribute()方法获得单一属性。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
try {
long size = (Long)Files.getAttribute(path, "basic:size", NOFOLLOW_LINKS);
System.out.println("Size: " + size);
} catch (IOException e) {
System.err.println(e);
}

  下面是一些基本的属性列表。

  • lastModifiedTime
  • lastAccessTime
  • creationTime
  • size
  • isRegularFile
  • isDirectory
  • isSymbolicLink
  • isOther
  • fileKey

  更新一个基本属性

  可以使用setTimes() 来更新任何或是所有文件的最后修改日期,上次访问日期,还有创建日期。这个方法用FileTime类的实例来表示这三个时间。如果三个参数

lastModifiedTime, lastAccessTim 或 creationTime 设置为null,那么对应的时间戳不会发生改变。

 import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributeView;
import java.nio.file.attribute.FileTime;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
long time = System.currentTimeMillis();
FileTime fileTime = FileTime.fromMillis(time);
try {
Files.getFileAttributeView(path,
BasicFileAttributeView.class).setTimes(fileTime, fileTime, fileTime);
} catch (IOException e) {
System.err.println(e);
}

  也可以使用Files.setLastModifiedTime()来更新文件的最后修改日期。

 long time = System.currentTimeMillis();
FileTime fileTime = FileTime.fromMillis(time);
try {
Files.setLastModifiedTime(path, fileTime);
} catch (IOException e) {
System.err.println(e);
}

  还有,可以使用setAttribute() 来更新文件的最后的修改时间。实际上,这个方法还可以更新文件的最后访问时间,和创建时间。

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

try {
Files.setAttribute(path, "basic:lastModifiedTime", fileTime, NOFOLLOW_LINKS);
Files.setAttribute(path, "basic:creationTime", fileTime, NOFOLLOW_LINKS);
Files.setAttribute(path, "basic:lastAccessTime", fileTime, NOFOLLOW_LINKS);
} catch (IOException e) {
System.err.println(e);
}

  接下来,可以使用getAttribute()方法来获取这三个时间的属性。

try {
FileTime lastModifiedTime = (FileTime)Files.getAttribute(path,
"basic:lastModifiedTime", NOFOLLOW_LINKS);
FileTime creationTime = (FileTime)Files.getAttribute(path,
"basic:creationTime", NOFOLLOW_LINKS);
FileTime lastAccessTime = (FileTime)Files.getAttribute(path,
"basic:lastAccessTime", NOFOLLOW_LINKS); System.out.println("New last modified time: " + lastModifiedTime);
System.out.println("New creation time: " + creationTime);
System.out.println("New last access time: " + lastAccessTime); } catch (IOException e) {
System.err.println(e);
}

  DOS 视图

  DOS视图在basic视图的基础上拓展四个属性,他们对应方法如下:

  • isReadOnly():
  • isHidden():
  • isArchive():
  • isSystem():

  直接上代码:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.DosFileAttributes;
...
DosFileAttributes attr = null;
Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt"); try {
attr = Files.readAttributes(path, DosFileAttributes.class);
} catch (IOException e) {
System.err.println(e);
} System.out.println("Is read only ? " + attr.isReadOnly());
System.out.println("Is Hidden ? " + attr.isHidden());
System.out.println("Is archive ? " + attr.isArchive());
System.out.println("Is system ? " + attr.isSystem());

  也可以设置单一的属性,对应的属性列表如下:

  • hidden
  • readonly
  • system
  • archive

  代码演示:

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

//setting the hidden attribute to true
try {
Files.setAttribute(path, "dos:hidden", true, NOFOLLOW_LINKS);
} catch (IOException e) {
System.err.println(e);
} //getting the hidden attribute
try {
boolean hidden = (Boolean) Files.getAttribute(path, "dos:hidden", NOFOLLOW_LINKS);
System.out.println("Is hidden ? " + hidden);
} catch (IOException e) {
System.err.println(e);
}

  文件拥有者视图

  大多数文件系统接受文件所有者身份的概念用于确定对象的访问权限。NIO.2通过UserPrincipal这个接口映射了这个概念,并且允许你使用FileOwnerAttributeView接口获取和设置文件的拥有者。如下面代码所示,有多种方法来获取和设置一个文件的所有者权限。

  使用Files.setOwner()方法来设置一个文件的拥有者。除了文件路径,这个方法获取UserPrincipal的实例用来映射文件拥有者的字符串表示。用户主名默认系统的服务通过FileSystem.getUserPrincipalLookupService()方法。下面是具体的例子。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserPrincipal;
...
UserPrincipal owner = null;
Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
try {
owner = path.getFileSystem().getUserPrincipalLookupService().
lookupPrincipalByName("apress"); // apress is a file owner, you can set the file owner according to you machine.
Files.setOwner(path, owner);
} catch (IOException e) {
System.err.println(e);
}

  使用 FileOwnerAttributeView.setOwner()方法设置文件拥有者

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileOwnerAttributeView;
import java.nio.file.attribute.UserPrincipal;
...
UserPrincipal owner = null;
Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
FileOwnerAttributeView foav = Files.getFileAttributeView(path,
FileOwnerAttributeView.class);
try {
owner = path.getFileSystem().getUserPrincipalLookupService().
lookupPrincipalByName("apress");
foav.setOwner(owner);
} catch (IOException e) {
System.err.println(e);
}

  使用Files.setAttribute()来设置文件拥有者

owner:owner, as you can see here:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserPrincipal;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

UserPrincipal owner = null;
Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
try {
owner = path.getFileSystem().getUserPrincipalLookupService().
lookupPrincipalByName("apress");
Files.setAttribute(path, "owner:owner", owner, NOFOLLOW_LINKS);
} catch (IOException e) {
System.err.println(e);
}

  使用FileOwnerAttributeView.getOwner()方法获取文件拥有者

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileOwnerAttributeView;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
FileOwnerAttributeView foav = Files.getFileAttributeView(path,
FileOwnerAttributeView.class);
try {
String owner = foav.getOwner().getName();
System.out.println(owner);
} catch (IOException e) {
System.err.println(e);
}

  也可以使用 Files.getAttribute()方法获取文件的拥有者

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.UserPrincipal;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

Path path = Paths.get("C:/rafaelnadal/tournaments/2009", "BNP.txt");
try {
UserPrincipal owner = (UserPrincipal) Files.getAttribute(path,
"owner:owner", NOFOLLOW_LINKS);
System.out.println(owner.getName());
} catch (IOException e) {
System.err.println(e);
}

  POSIX视图

  给Unix粉丝们带来了好消息。POSIX视图在继承了basic视图的基础上扩展了自己的属性。包括文件拥有者,文件所属组,以及其他9个相关的访问权限。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.PosixFileAttributes;

PosixFileAttributes attr = null;
Path path = Paths.get("/home/rafaelnadal/tournaments/2009/BNP.txt");
try {
attr = Files.readAttributes(path, PosixFileAttributes.class);
} catch (IOException e) {
System.err.println(e);
} System.out.println("File owner: " + attr.owner().getName());
System.out.println("File group: " + attr.group().getName());
System.out.println("File permissions: " + attr.permissions().toString());

  也可以使用Files.getFileAttributeView()方法。

import java.nio.file.attribute.PosixFileAttributeView;

try {
attr = Files.getFileAttributeView(path,
PosixFileAttributeView.class).readAttributes();
} catch (IOException e) {
System.err.println(e);
}

  permissions()方法返回了PosixFilePermissions对象的一个集合。PosixFilePermissions是一个权限帮助类。其中一个比较有用的方法是asFileAttribute(),它可以接受一个文件属性构建的权限的set对象传递给 Path.createFile()或 Path.createDirectory()方法。如下例所示,你可以获取一个文件的POSIX权限并且使用这个相同的权限去创建一个新的文件。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;

Path new_path = Paths.get("/home/rafaelnadal/tournaments/2009/new_BNP.txt");
FileAttribute<Set<PosixFilePermission>> posixattrs =
PosixFilePermissions.asFileAttribute(attr.permissions());
try {
Files.createFile(new_path, posixattrs);
} catch (IOException e) {
System.err.println(e);
}

  还可以使用fromString()方法传递一个硬编码的字符串权限信息。

Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rw-r--r--");
try {
Files.setPosixFilePermissions(new_path, permissions);
} catch (IOException e) {
System.err.println(e);
}

 POSIX组拥有者

 文件组拥有者可以使用属性名为“group”来设置。setGroup()方法得到文件的路径和一个GroupPrincipal实例,这个实例映射一个字符串代表组所有者。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.GroupPrincipal;
import java.nio.file.attribute.PosixFileAttributeView;

Path path = Paths.get("/home/rafaelnadal/tournaments/2009/BNP.txt");
try {
GroupPrincipal group = path.getFileSystem().
getUserPrincipalLookupService().lookupPrincipalByGroupName("apressteam");
Files.getFileAttributeView(path, PosixFileAttributeView.class).setGroup(group);
} catch (IOException e) {
System.err.println(e);
}

  你可以很容易地使用Files.getAttribute()方法来获取组的属性。

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;

try {
GroupPrincipal group = (GroupPrincipal) Files.getAttribute(path, "posix:group",
NOFOLLOW_LINKS);
System.out.println(group.getName());
} catch (IOException e) {
System.err.println(e);
}

  除此而外,还有ACL视图,文件存储视图,用户自定义视图,基本上用法跟上面视图类似,不再详叙。

  完。