Java SE 7文件操作新特性之管理元数据
转自 开发者的天空
管理元数据(文件属性和文件存储属性)
在文件系统中,文件或者目录的元数据是和文件或者目录本身存储在一起的,而且元数据保存了很多的信息,例如: 对象是文件还是目录,抑或是符号链接。文件的大小、创建 时间、最后修改时间、文件的所有者、组、访问权限等。
java.nio.file.attribute包提供了访问和管理文件系统元数据(通常叫做文件属性)的功能。不同的文件系统提供的文件属性是不一样 的,所以我们按照这个将文件的属性划分成了不同的视图(View)。每个View对应一个文件系统的实现,如POSIX(Unix使用的文件系统)或 DOS;或者是所有文件系统共有的属性,如文件的所有权。当我们读取文件的属性的时候,是一次读取整个视图的属性的。 Java所支持的视图有:
BasicFileAttributeView – 所有的文件系统实现都必须提供的属性的视图。
DosFileAttributeView – 扩展了BasicFileAttributeView,添加了DOS文件系统能够支持的几种属性。
PosixFileAttributeView – 扩展了BasicFileAttributeView,添加了POSIX文件系统能够支持的几种属性。
FileOwnerAttributeView – 任何文件系统实现都会支持文件所有者的属性。这个View就包含这个属性。
AclFileAttributeView – 支持读取和更新文件的访问控制列表。支持NFSv4访问控制列表模型。所有定义了到NFSv4的映射的访问控制列表模型也同样支持,如Windows的访 问控制列表模型。
UserDefinedFileAttributeView – 支持用户自定义的元数据。这个View可以被映射到文件系统提供的扩展机制。例如在Solaris操作系统上,可以将这个View存储MIMEtype。
不同的文件系统可能支持不同的一个或几个view,也有可能含有上面所有的View都没有包含的文件属性。
Attributes类
大多数情况下,我们不需要直接使用FileAttributeView接口,Attributes类提供了转换的方法来读取和设置文件属性。
下面是Attributes类提供的一些方法:
读取或设置基本的文件属性:
readBasicFileAttributes(FileRef, LinkOption...)
setLastAccessTime(FileRef, FileTime)
setLastModifiedTime(FileRef, FileTime)
读取或设置POSIX文件属性
readPosixFileAttributes(FileRef, LinkOption...)
setPosixFilePermissions(FileRef, Set<PosixFilePermission>)
读取或设置文件的所有者
getOwner(FileRef)
setOwner(FileRef, UserPrincipal)
一次读取所有的文件属性
readAttributes(FileRef, String, LinkOption...)
读取或设定特定的文件属性
getAttribute(FileRef, String, LinkOption...)
setAttribute(FileRef, String, Object)
读取DOS文件属性
readDosFileAttributes(FileRef, LinkOption...)
读取或设置文件的访问控制列表。
getAcl(FileRef)
setAcl(FileRef, List<AclEntry>)
读取文件存储空间的属性,如总空间、有效空间、未分配空间等。
readFileStoreSpaceAttributes(FileStore)
每个read方法都返回一个对象,该对象会提供访问方法,我们通过这些访问方法能够很方便的获得特定的文件属性的值。
要想读取基本的文件信息,那么可以调用readBasicFileAttributes(FileRef, LinkOption...)方法。这个方法支持的LinkOption就只有NOFOLLOW_LINKS。这个方法会一次性的读取所有的基本的文件属 性并返回一个BasicFileAttributes对象,我们可以访问该对象获取具体的文件属性。如果程序对文件没有访问权限,该方法会抛出 SecurityException异常。要注意的是,并不是所有文件系统的实现都支持创建时间、最后修改时间和最后访问时间这三个属性。如果某个属性不 被支持,则调用该属性的get方法时会返回null。下面就是一个读取文件的基本属性的例子:
Path file
=
;
BasicFileAttributes attr = Attributes.readBasicFileAttributes(file);
if (attr.creationTime() != null ) {
System.out.println( " creationTime: " + attr.creationTime());
}
if (attr.lastAccessTime() != null ) {
System.out.println( " lastAccessTime: " + attr.lastAccessTime());
}
if (attr.lastModifiedTime() != null ) {
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());
下面的例子中,我们检查了对一个文件的访问权限,判断 该文件是常规的文件还是目录:

BasicFileAttributes attr = Attributes.readBasicFileAttributes(file);
if (attr.creationTime() != null ) {
System.out.println( " creationTime: " + attr.creationTime());
}
if (attr.lastAccessTime() != null ) {
System.out.println( " lastAccessTime: " + attr.lastAccessTime());
}
if (attr.lastModifiedTime() != null ) {
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());
import
static
java.nio.file.AccessMode.
*
;
Path file =
;
boolean error = false ;
try {
file.checkAccess(READ, EXECUTE);
if ( ! Attributes.readBasicFileAttributes(file).isRegularFile()) {
error = true ;
}
} catch (IOException x) {
// Logic for error condition
error = true ;
}
if (error) {
// Logic for failure
return ;
}
// Logic for executable file
Path file =

boolean error = false ;
try {
file.checkAccess(READ, EXECUTE);
if ( ! Attributes.readBasicFileAttributes(file).isRegularFile()) {
error = true ;
}
} catch (IOException x) {
// Logic for error condition

error = true ;
}
if (error) {
// Logic for failure

return ;
}
// Logic for executable file

设置 时间戳
前面的文件基本属性的代码中演示了怎样获取文件的时间戳,Attributes类还提供了两个方法来设置时间戳:setLastAccessTime和 setLastModifiedTime,下面是这两个方法的示例:
Path file
=
;
BasicFileAttributes attr = Attributes.readBasicFileAttributes(file);
long currentTime = System.currentTimeMillis();
if (attr.lastModifiedTime() != null ) {
FileTime ft = FileTime.fromMillis(currentTime);
Attributes.setLastModifiedTime(file, ft);
} else {
System.err.println( " lastModifiedTime time stamp not supported " );
}

BasicFileAttributes attr = Attributes.readBasicFileAttributes(file);
long currentTime = System.currentTimeMillis();
if (attr.lastModifiedTime() != null ) {
FileTime ft = FileTime.fromMillis(currentTime);
Attributes.setLastModifiedTime(file, ft);
} else {
System.err.println( " lastModifiedTime time stamp not supported " );
}
DOS的文件属性
要获取一个文件的DOS的文件属性,需要调用readDosFileAttributes方法。这个方法会返回一个DosFileAttributes对 象,该对象提供了获取DOS文件属性的方法,例如:
Path file
=
;
try {
DosFileAttributes attr = Attributes.readDosFileAttributes(file);
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 (IOException x) {
System.err.println( " DOS file attributes not supported: " + x);
}

try {
DosFileAttributes attr = Attributes.readDosFileAttributes(file);
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 (IOException x) {
System.err.println( " DOS file attributes not supported: " + x);
}
我 们可以使用setAttribute方法来设置DOS文件属性,如:
Path file
=
;
Attributes.setAttribute(file, " dos:hidden " , true );

Attributes.setAttribute(file, " dos:hidden " , true );
要注意的是,不是只有DOS操作系统才支持DOS文 件属性,有些操作系统如Samba也支持DOS文件属性。
POSIX的文件属性
POSIX是Portable Operation System Interface for UNIX的缩写,而且IEEE和ISO定义很多标准来保证不同的UNIX之间的户操作性,因此对于开发人员来说,针对POSIX编写的程序能够很容易的运 行在不同的兼容POSIX的文件系统上。要读取POSIX文件属性,需要调用readPosixFileAttributes方法。除了文件所有者和所属组,POSIX还支持9种文件权限许可组 合:读、写、执行三种权限和文件所有者、同组的用户和其他用户三种角色的组合(3 × 3 = 9)。下面就是读取POSIX文件属性的简单的例子:
Path file
=
;
PosixFileAttributes attr = Attributes.readPosixFileAttributes(file);
System.out.format( " %s %s %s%n " , attr.owner().getName, attr.group().getName(),
PosixFilePermissions.toString(attr.permissions()));

PosixFileAttributes attr = Attributes.readPosixFileAttributes(file);
System.out.format( " %s %s %s%n " , attr.owner().getName, attr.group().getName(),
PosixFilePermissions.toString(attr.permissions()));
下面的代码读取了一个文件的属性,然后创建了一个新的 文件,将原有的文件的权限属性赋予新创建的文件:
Path sourceFile
=
;
Path newFile =
;
PosixFileAttributes attrs = Attributes.readPosixFileAttributes(sourceFile);
FileAttribute < Set < PosixFilePermission >> attr =
PosixFilePermissions.asFileAttribute(attrs.permissions());
try {
file.createFile(attr);
} catch (IOException x) {
// unable to create the file
}
上 面的代码中我们使用了PosixFilePermission类,该类是一个帮助类,提供了一些方法来读取和生成文件权限,这里就不详细解释了。

Path newFile =

PosixFileAttributes attrs = Attributes.readPosixFileAttributes(sourceFile);
FileAttribute < Set < PosixFilePermission >> attr =
PosixFilePermissions.asFileAttribute(attrs.permissions());
try {
file.createFile(attr);
} catch (IOException x) {
// unable to create the file
}
如果想指定创建的文件的权限,可以使用下面的代码:
Path file
=
;
Set < PosixFilePermission > perms = PosixFilePermissions.fromString( " rw------- " );
FileAttribute < Set < PosixFilePermission >> attr = PosixFilePermissions.asFileAttribute(perms);
try {
Attributes.setPosixFilePermissions(file, perms);
} catch (IOException x) {
System.err.println(x);
}
文 件有所有者的属性和所属组的属性,在设置这些属性的时候,我们需要传入一个UserPrincipal对象作为参数,我们可以使用 UserPrincipalLookupService来根据用户名或组名来创建该对象。UserPrincipalLookupService实例可以 通过FileSystem.getUserPrincipalLookupService方法获得。下面就是设置所有者属性的例子:

Set < PosixFilePermission > perms = PosixFilePermissions.fromString( " rw------- " );
FileAttribute < Set < PosixFilePermission >> attr = PosixFilePermissions.asFileAttribute(perms);
try {
Attributes.setPosixFilePermissions(file, perms);
} catch (IOException x) {
System.err.println(x);
}
Path file
=
;
try {
UserPrincipal owner = file.GetFileSystem().getUserPrincipalLookupService()
.lookupPrincipalByName( " sally " );
Attributes.setOwner(file, owner);
} catch (IOException x) {
System.err.println(x);
}
Attributes 类没有提供设置所属组的方法,如果要设置所属组,需要调用POSIX文件属性视图来进行,下面是示例代码:

try {
UserPrincipal owner = file.GetFileSystem().getUserPrincipalLookupService()
.lookupPrincipalByName( " sally " );
Attributes.setOwner(file, owner);
} catch (IOException x) {
System.err.println(x);
}
Path file
=
;
try {
GroupPrincipal group = file.getFileSystem().getUserPrincipalLookupService()
.lookupPrincipalByGroupName( " green " );
file.getFileAttributeView(PosixFileAttributeView. class ).setGroup(group);
} catch (IOException x) {
System.err.println(x);
}

try {
GroupPrincipal group = file.getFileSystem().getUserPrincipalLookupService()
.lookupPrincipalByGroupName( " green " );
file.getFileAttributeView(PosixFileAttributeView. class ).setGroup(group);
} catch (IOException x) {
System.err.println(x);
}
用户定义的文件属性
如果文件系统支持的属性对你来说还不够用,你可以通过UserDefinedAttributeView来创建和跟踪自己的文件属性。
下面的例子中,我们将MIME类型作为一个用户自定义的属性:
Path file
=
;
UserDefinedFileAttributeView view = file
.getFileAttributeView(UserDefinedFileAttributeView. class );
view.write( " user.mimetype " , Charset.defaultCharset().encode( " text/html " );
要读取MIME类型属性,要使用以下的代码:

UserDefinedFileAttributeView view = file
.getFileAttributeView(UserDefinedFileAttributeView. class );
view.write( " user.mimetype " , Charset.defaultCharset().encode( " text/html " );
Path file
=
;
UserDefinedFileAttributeView view = file
.getFileAttributeView(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();
如果文件系统不支持扩展属性,那么会抛出一个 UnsupportedOperationException异常,你可以咨询系统管理员来确认系统是否支持文件的扩展属性并进行相应的配置。

UserDefinedFileAttributeView view = file
.getFileAttributeView(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();
文件存储属性
文件存储属性其实我们应该非常熟悉的属性,我们查看硬盘属性的时候,经常看到硬盘总的存储空间,使用了的存储空间,空闲的存储空间等就是文件存储属性。下 面是获取文件存储属性的代码:
Path file
=
;
FileStore store = file.getFileStore();
FileStoreSpaceAttributes attr = Attributes.readFileStoreSpaceAttributes(store);
System.out.println( " total: " + attr.totalSpace() );
System.out.println( " used: " + (attr.totalSpace() - attr.unallocatedSpace()) );
System.out.println( " avail: " + attr.usableSpace() );

FileStore store = file.getFileStore();
FileStoreSpaceAttributes attr = Attributes.readFileStoreSpaceAttributes(store);
System.out.println( " total: " + attr.totalSpace() );
System.out.println( " used: " + (attr.totalSpace() - attr.unallocatedSpace()) );
System.out.println( " avail: " + attr.usableSpace() );