The definition of metadata is "data about other data." In the case of a file system, the data is contained in its files and directories, and the metadata tracks information about each of these objects: is it a regular file, a directory or a link? What is its size, creation date, last modified date, file owner, group owner, access permissions?The
java.nio.file.attribute
package provides an API to manage the file system's metadata, or file attributes, as it is typically called. Because different file systems have different notions about which attributes should be tracked, related file attributes are grouped together into views. A view maps to a particular file system implementation, such as POSIX or DOS, or to a common functionality, such as file ownership.A view's attributes are read in one bulk operation. This allows for efficient access and also allows for bi-directional mapping for cases where the file attributes must be converted to and from the file system implementation.
The supported views are:
BasicFileAttributeView
— provides a view of basic attributes required to be supported by all file system implementations.DosFileAttributeView
— extends the basic attribute view with the standard four bits supported on file systems that support the DOS attributes.PosixFileAttributeView
— extends the basic attribute view with attributes supported on file systems that support the POSIX family of standards, such as UNIX. These attributes include file owner, group owner, and the nine related access permissions.FileOwnerAttributeView
— supported by any file system implementation that supports the concept of a file owner.AclFileAttributeView
— supports reading or updating a file's Access Control Lists (ACL). The NFSv4 ACL model is supported. Any ACL model, such as the Windows ACL model, that has a well defined mapping to the NFSv4 model may also be supported. Discussion of this view is beyond the scope of this tutorial, but theAclEdit
example is included for you to study.UserDefinedFileAttributeView
— allows support of metadata that is user defined — this may be mapped to any extension mechanisms that a system supports. On Solaris, for example, you can use this to store the MIME type of a file.A specific file system implementation may support only the basic file attribute view, or it may support several of these file attribute views, or it may support other attribute views not included in this API.
This section covers:
- The Attributes Class
- Basic File Attributes
- Setting Timestamps
- DOS File Attributes
- User Defined File Attributes
- POSIX File Permissions
- Setting a File or Group Owner
- File Store Attributes
The Attributes Class
In most instances, you will not have to deal directly with any of the
FileAttributeView
interfaces. TheAttributes
class provides convenience methods for reading and setting file attributes. The examples in this lesson use theAttributes
methods:
Method Comment readBasicFileAttributes(FileRef, LinkOption...)
setLastAccessTime(FileRef, long, TimeUnit)
setLastModifiedTime(FileRef, long, TimeUnit)
Reads or sets basic file attributes. readPosixFileAttributes(FileRef, LinkOption...)
setPosixFilePermissions(FileRef, Set<PosixFilePermission>)
Reads or sets a file's POSIX attributes. getOwner(FileRef)
setOwner(FileRef, UserPrincipal)
Reads or sets the owner of the file. readAttributes(FileRef, String, LinkOption...)
Reads an entire set of attributes in one operation. getAttribute(FileRef, String, LinkOption...)
setAttribute(FileRef, String, Object)
Reads or sets the value of a particular file attribute. readDosFileAttributes(FileRef, LinkOption...)
Reads a file's DOS attributes. getAcl(FileRef)
setAcl(FileRef, List<AclEntry>)
Reads or sets a file's Access Control List (ACL). The example shows how to edit a file's ACL. You can try the example on Solaris ZFS, Microsoft Windows NTFS, or any platform that supports Access Control Lists.
AclEdit
readFileStoreSpaceAttributes(FileStore)
Reads the space attributes (total, available, and unallocated space) of a file store. Each of these
read...
methods (ie:readDosFileAttributes
) returns an object (ie.DosFileAttributes
) that provides accessor methods so you can easily get the values of the specific file attributes.
Basic File Attributes
To read the basic attributes of a file, you can use the
readBasicFileAttributes(FileRef, LinkOption...)
method which reads all the basic attributes in one bulk operation — this is far more efficient than accessing the file system separately to read each each individual attribute. The varargs argument currently supports theLinkOption
enum,NOFOLLOW_LINKS
. Use this option when you do not want symbolic links to be followed.
A word about timestamps: The set of basic attributes includes three timestamps:creationTime
,lastModifiedTime
, andlastAccessTime
. Any of these timestamps may not be supported for a particular implementation, in which case the corresponding accessor method returns-1
. To decode the time stamp, which is returned as along
, you must also examine theresolution
value, which indicates whether the time stamp is retrieved in milliseconds (most common), nanoseconds (becoming more common), or some otherTimeUnit
format.The
readBasicFileAttributes
method returns an instance ofBasicFileAttributes
which provides a number of methods for retrieving the attribute information. The following code snippet reads and prints the basic file attributes for a given file. TheprintTime
method converts thelong
time values to a string and prints them out.
import java.util.concurrent.*; //Converts a time stamp, represented as along
value, //to a human readable string and prints it to standard out. //The 'msg' arg is a string that describes the time stamp. //The 'resolution' arg is a TimeUnit enum indicating whether the 'time' //value is in nanoseconds or milliseconds. This method does not //support other time formats. static void printTime(long time, TimeUnit resolution, String msg) { if (time == -1) { System.err.println(msg + "not supported"); return; } if (resolution == TimeUnit.NANOSECONDS) { //Convert the timestamp from nanoseconds to milliseconds. long mstime = resolution.convert(time, TimeUnit.MILLISECONDS); //The Date class uses milliseconds. Date date = new Date(mstime); System.out.println(msg + date.toString()); } else if (resolution == TimeUnit.MILLISECONDS) { Date date = new Date(time); System.out.println(msg + date.toString()); } else { //This method supports only timestamps in ms or ns formats. System.err.println(msg + "unsupported resolution - " + resolution); } } ... Path file = ...; BasicFileAttributes attr = Attributes.readBasicFileAttributes(file); TimeUnit resolution = attr.resolution(); printTime(attr.creationTime(), resolution, "creationTime: "); printTime(attr.lastAccessTime(), resolution, "lastAccessTime: "); printTime(attr.lastModifiedTime(), resolution, "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("linkCount: " + attr.linkCount()); System.out.println("resolution: " + resolution); System.out.println("size: " + attr.size());In addition to the accessor methods shown in this example, there is a
fileKey
method that returns an object that uniquely identifies the file ornull
if no file key is available.In the
Checking File Accessibility
section, we showed an example that checked whether a file was executable by invoking thecheckAccess
method to examine the file'sREAD
andEXECUTE
bits. Of course, on UNIX, directories also use the execute bit to confer access permission to the directory. To check that a file is a regular file that can be executed (and not a directory), the example is extended to check the file type using the basic file attributes:
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...
Setting Timestamps
The previous section shows how you can read timestamps using the
readBasicFileAttributes
method and how theresolution
attribute is essential when examining the time stamp values.The
Attributes
class provides two methods for setting timestamps:setLastAccessTime
andsetLastModifiedTime
. The following code snippet sets the last modified time on a file system that uses milliseconds or nanoseconds.
import java.util.concurrent.*; Path file = ...; BasicFileAttributes attr = Attributes.readBasicFileAttributes(file); long currentTime = System.currentTimeMillis(); if (attr.resolution() == TimeUnit.MILLISECONDS) { Attributes.setLastModifiedTime(file, currentTime, TimeUnit.MILLISECONDS); } else if (attr.resolution() == TimeUnit.NANOSECONDS) { Attributes.setLastModifiedTime(file, TimeUnit.MILLISECONDS.toNanos(currentTime), TimeUnit.NANOSECONDS); } else { System.err.println("time format not supported: " + attr.resolution()); }
DOS File Attributes
DOS file attributes are also supported on file systems other than DOS, such as Samba.
To read the DOS attributes of a file, you can use the
readDosFileAttributes(FileRef, LinkOption...)
method. You can specify theNOFOLLOW_LINKS
link option if you do not want any symbolic links to be followed.This method returns an instance of
DosFileAttributes
which provides methods for retrieving the DOS attributes: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); }There are no special-purpose methods for setting a DOS attribute, but you can accomplish this using the
setAttribute(FileRef, String, Object)
method:Path file = ...; Attributes.setAttribute(file, "dos:hidden", true);
POSIX File Permissions
POSIX is an acronym for Portable Operating System Interface for Unix and is a set of IEEE and ISO standards designed to ensure interoperability among different flavors of Unix. If a program is created to conform to the set of POSIX standards, it should be easily ported to other POSIX-compliant operating systems.
To read the POSIX file attributes, you can use the
readPosixFileAttributes(FileRef, LinkOption...)
method. Besides file owner and group owner, POSIX supports nine file permissions: read, write and execute permissions for the file owner, members of the same group, and "everyone else".The following code snippet reads the POSIX file attributes for a given file and prints them to standard out:
Path file = ...; PosixFileAttributes attr = Attributes.readPosixFileAttributes(file); System.out.format("%s %s %s%n", attr.owner().getName, attr.group().getName(), PosixFilePermissions.toString(attr.permissions()));The
PosixFilePermissions
helper class provides several useful methods:
- The
toString
method, used in the previous code snippet, converts the file permissions to a string (ie:rw-r--r--
).- The
fromString
method accepts a string representing the file permissions and constructs aSet
of file permissions.- The
asFileAttribute
method accepts aSet
of file permissions and constructs a file attribute that can be passed to thePath.createFile
orPath.createDirectory
methods.The following code reads the attributes from one file, and creates a new file, assigning the attributes from the original file to the new file:
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 }The
asFileAttribute
method wraps the permissions as aFileAttribute
. The code then attempts to create a new file with those permissions. Note that theumask
also applies so the new file may be more secure than the permissions that were requested.To set a file's permissions to values represented as a hard-coded string, you can use the following code:
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); }The
example recursively changes the permissions of files in a manner similar to the
Chmod
chmod
utility.
Setting a File or Group Owner
How do you translate a name into something you can store as a file owner or a group owner? You can use
UserPrincipalLookupService
. This service looks up a name or group name as a string and returns aUserPrincipal
object representing that string. You can obtain the user principal lookup service for the default file system using theFileSystem.getUserPrincipalLookupService
method.The following code snippet shows how to set the file owner using the
setOwner
method:
Path file = ...; try { UserPrincipal owner = file.GetFileSystem().getUserPrincipalLookupService() .lookupPrincipalByName("sally"); Attributes.setOwner(file, owner); } catch (IOException x) { System.err.println(x); }There is no special-purpose method in the
Attributes
class for setting a group owner, but there is a type safe way to do it directly on the POSIX file attribute view:
Path file = ...; try { GroupPrincipal group = file.getFileSystem().getUserPrincipalLookupService() .lookupPrincipalByGroupName("green"); file.getFileAttributeView(PosixFileAttributeView.class).setGroup(group); } catch (IOException x) { System.err.println(x); }
User Defined File Attributes
If the file attributes supported by your particular file system implementation aren't sufficient for your needs, you can use the
UserDefinedAttributeView
to create and track your own file attributes.Some implementations map this concept to features like NTFS Alternative Data Streams and extended attributes on file systems such as ext3 and ZFS. Most implementations impose restrictions on the size of the value — for example, ext3 limits the size to 4k.
A file's MIME type can be stored as a user-defined attribute using this code snippet:
Path file = ...; UserDefinedFileAttributeView view = file .getFileAttributeView(UserDefinedFileAttributeView.class); view.write("user.mimetype", Charset.defaultCharset().encode("text/html");To read the MIME type attribute:
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();The
example shows how to get, set and delete a user-defined attribute.
Xdd
Note: On Linux, you may have to enable extended attributes for user-defined attributes to work. If you get anUnsupportedOperationException
when trying to access the user defined attribute view, you need to remount the file system. The following command remounts the root partition with extended attributes (for the ext3 file system). If this does not work for your flavor of Linux, consult the documentation.
If you want to make the change permanent, add an entry to$ sudo mount -o remount,user_xattr //etc/fstab
.
File Store Attributes
You can use the
readFileStoreSpaceAttributes(FileStore)
method to learn how much space is available on a file store.The following code snippet prints out the space usage for the file store where a particular file resides:
Path file = ...; FileStore store = file.getFileStore(); FileStoreSpaceAttributes attr = Attributes.readFileStoreSpaceAttributes(store); long total = attr.totalSpace() / 1024; long used = (attr.totalSpace() - attr.unallocatedSpace()) / 1024; long avail = attr.usableSpace() / 1024; System.out.println("store: " + store); System.out.println("total: " + attr.totalSpace() / 1024); System.out.println("used: " + (attr.totalSpace() - attr.unallocatedSpace()) / 1024); System.out.println("avail: " + attr.usableSpace() / 1024);The
DiskUsage
example uses this API to print out disk space information for all the stores on the default file system. This example uses thegetFileStores
method in theFileSystem
class to fetch all the file stores for the the file system.