倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

これからの「Java I/O」の話をしようwww (7) : Files クラスのメソッド 〜続・ファイル、ディレクトリの属性〜

Java nio の API を見ていくシリーズ(目次)。 今回は、前回に引き続き、Files クラスに定義されている属性関連のメソッドを見ていきます。 正直、あんまり使わないかも知れませんが・・・

これらを使う際には、java.nio.file.attribute パッケージのクラスも使わないといけない場合があります。 このパッケージには少々分かりにくい命名の型がありますが、次の2つの違いを覚えておけばとりあえず大丈夫かと:

  • XxxxFileAttributes とそのサブタイプ ・・・ 読み取り専用
  • XxxxFileAttributeView とそのサブタイプ ・・・ 書き込み専用(ただし XxxxFileAttributes オブジェクトを取得可)

以下では、これらのタイプを属性タイプ、そのオブジェクトを属性オブジェクトと書くことにします(正式名称ではない)。

Files クラスに定義されている属性関連のメソッド

Files クラスに定義されている属性関連のメソッド定義を見ておきましょう:

package java.nio.file;

public final Class Files{
    ...

    //***** 属性の取得 *****
    public static
    Object getAttribute(Path path, String attribute, LinkOption... options)

    public static
    Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)

    public static <A extends BasicFileAttributes>
    A readAttributes(Path path, Class<A> type, LinkOption... options)

    //***** 属性の設定 *****
    public static
    Path setAttribute(Path path, String attribute, Object value, LinkOption... options)

    public static <V extends FileAttributeView>
    V getFileAttributeView(Path path, Class<V> type, LinkOption... options)
}

BasicFileAttributes を返す2つ目の readAttributes() メソッドと最後の getFileAttributeView() メソッドは属性タイプを指定してその属性オブジェクトを取得するメソッドです。 これらを使う場合には、java.nio.file.attribute パッケージのクラスをガッツリ使う必要があります。 この2つ以外のメソッドでも場合によっては java.nio.file.attribute パッケージのお世話にならないといけない場合もありますが。

サンプル・コード
ではこれらのメソッドを使ったサンプルを見ていきましょう。 拙者の環境の都合上、DOS ファイルシステム用のサンプル・コードになってます。 例によって Groovy で書いてます:

import java.nio.file.Files
import java.nio.file.attribute.DosFileAttributes
import java.nio.file.attribute.DosFileAttributeView
import java.nio.file.attribute.FileTime

// 準備
// 一時ファイルを作成、適当に内容を書き込み
def src = Files.createTempFile(null, null)
Files.write(src, ["abcdefghijklmnopqrstuvwxyz"]*100)


//***** getAttribute() *****
// 属性名を文字列で指定
println("size: " + Files.getAttribute(src, "size"))  // 「size: 2800」と表示


//***** readAttributes() : 属性を文字列で指定 *****
// 属性名のリストをコンマで区切って指定(スペースも入れちゃダメ)
println("attributes: " + Files.readAttributes(src, "size,lastModifiedTime,isRegularFile"))
  // 「attributes: [lastModifiedTime:2015-10-19T02:04:37Z, size:2800, isRegularFile:true]」
  // などと表示。 readAttributes() の返り値は Map オブジェクト。


//***** readAttributes() : 属性を属性タイプで指定 *****
// 属性タイプを指定して、その型の属性オブジェクトを取得
DosFileAttributes dos = Files.readAttributes(src, DosFileAttributes.class);

println("size            : "  +dos.size())    // 各メソッドで属性にアクセス
println("lastModifiedTime: " + dos.lastModifiedTime())
println("isRegularFile   : " + dos.isRegularFile())
println("isArchive       : " + dos.isArchive());


//***** setAttribute() *****
def aWeekAgo = FileTime.fromMillis(System.currentTimeMillis()-7*24*60*60*1000L)
Files.setAttribute(src, "lastModifiedTime", aWeekAgo)    // 属性名を文字列で指定
println("lastModified: " + Files.getLastModifiedTime(src))
  // 「lastModified: 2015-10-12T02:04:37.808Z」などと表示


//***** getAttributeView() *****
println("isWritable (before): " + Files.isWritable(src))
  // 「isWritable (before): true」と表示。 変更前は書き込み可能

DosFileAttributeView dosView = Files.getFileAttributeView(src, DosFileAttributeView.class)
  // 属性タイプを指定してその型のオブジェクトを取得
dosView.setReadOnly(true)  // AttributeView オブジェクトに変更を加える

println("isWritable (after) : " + Files.isWritable(src))
  // 「isWritable (after) : false」と表示。 変更後書き込み不可。
 // view に対する変更が元のファイルに反映される
  • 属性の名前を文字列で指定する getAttribute, readAttribute (String 版), setAttribute は、手軽に使えますが指定した文字列をタイポしてもコンパイラが検出してくれません。
  • 一方、属性クラスを指定して属性オブジェクトを取得すると、属性名に対応した名前のメソッドを使って属性にアクセスできるのでタイポした場合はコンパイラが検出してくれます。

属性を指定する文字列としてどんなものが可能なのかは下記の属性、もしくは属性ビューのところでで簡単に見ます。 もしくはそれぞれの(Attributes, AttributeView の) JavaDoc を参照のこと。

ファイルの属性

ファイルの属性は BasicFileAttributes とそのサブタイプで表されます:

  • BasicFileAttributes
    • PosixFileAttributes
    • DosFileAttributes

このツリー構造は継承関係を示してます。 BasicFileAttributes とそのサブタイプは、後で出てくる AttributeView とは異なり読み取り専用のオブジェクトです。 BasicFileAttributes にはどのファイルシステムでもサポートされている属性が定義されています。 一方、PosixFileAttributes と DosFileAttributes には、それぞれの名前が指すファイルシステムに特有の属性が定義されています。 各型の定義は以下のようになっています:

package java.nio.file.attribute;

public interface BasicFileAttributes{

    long size();

    boolean isRegularFile();
    boolean isDirectory();
    boolean isSymbolicLink();
    boolean isOther();

    FileTime creationTime();
    FileTime lastAccessTime();
    FileTime lastModifiedTime();

    Object fileKey();
}

public interface DosFileAttributes extends BasicFileAttributes{
    boolean isReadOnly();
    boolean isHidden();
    boolean isArchive();
    boolean isSystem();
}

public interface PosixFileAttributes extends BasicFileAttributes{
    UserPrincipal owner();
    Set<PosixFilePermission> permissions();
    GroupPrincipal group();
}

簡単な使い方は、上記のサンプル・コードの Files#readAttributes() メソッド(2つ目)の箇所を参照。

属性ビュー

属性ビューは AttributeView とそのサブタイプで表されます:

  • AttributeView
    • FileAttributeView
      • BasicFileAttributeView
        • DosFileAttributeView
        • PosixFileAttributeView (FileOwnerAttributeView も拡張)
      • FileOwnerAttributeView
        • AclFileAttributeView
      • UserDefinedFileAttributeView
    • FileStoreAttributeView

これらの型のうち、実際に使いそうなのは太字にしている3つだと思います。 FileOwnerAttributeView は Unix 系の OS では PosixFileAttributeView として使えます(DOS 系だと最近のものは AclFileAttributeView としてサポートされてるようですね)。 これらの型には setter メソッドがいくつか定義されていて、属性値はこれらのメソッドを介して設定します。 この設定は元の Path オブジェクトが指すファイルに反映されます。 簡単な使い方は、上記サンプル・コードの Files#getAttributeView() メソッドの箇所参照。

太字の3つの FileAttributeView について、定義されている属性を表にしておきます(それぞれの JavaDoc 参照):

  • BasicFileAttributeView クラス (basic) の属性
属性 説明
size Long ファイルサイズ
isRegularFile Boolean 通常ファイルかどうか
isDirectory Boolean ディレクトリかどうか
isSymbolicLink Boolean シンボリックリンクかどうか
isOther Boolean 通常ファイル、ディレクトリ、シンボリックリンク以外のナニカであるかどうか
lastModifiedTime FileTime 最終更新時刻
lastAccessTime FileTime 最終アクセス時刻
creationTime FileTime 作成時刻
fileKey Object ファイルキー、サポートされてなければ null


  • DosFileAttributeView クラス (dos) の属性
属性 説明
readonly Boolean 読み取り専用かどうか
hidden Boolean 隠しファイルかどうか
archive Boolean アーカイブファイルかどうか
system Boolean システムファイルかどうか


  • PosixFileAttributeView クラス (posix) の属性
属性 説明
owner UserPrincipal 所有者(FileOwnerAttributeView から継承)
permissions Set<PosixFilePermission> POSIX ファイルパーミッション
group GroupPrincipal グループオーナー

実際に使う際には IDE のコンテンツアシストとかを多用してあれこれイジることになるでしょう。

AclFileAttributeView は「wikipedia:アクセス制御リスト (Access Controll List)」の実装のようです。 Acl に関連するクラスや列挙型が java.nio.file.attribute パッケージにいろいろ定義されてます。 全く試してません。 FileStoreAttributeView は FileStore をやるときが来れば触れるかと思います(と言いつつ、拙者の OS ではサポートされてないんだけど・・・)。

【追記】文字列による属性の指定

文字列を用いて属性を指定する3つメソッド

  • Files#getAttribute(Path, String, LinkOption...): Object
  • Files#readAttributes(Path, String, LinkOption...): Map<String, Object>
  • Files#setAttribute(Path, String, Object, LinkOption...): Path

では、属性名の文字列に FileAttributeView クラスに対応する文字列を接頭辞としてつけることで、その view に定義されている属性を参照することができます。

import java.nio.file.Files
import java.nio.file.AccessDeniedException

def path = Files.createTempFile(null, null)

//***** getAttribute() *****
println Files.getAttribute(path, "dos:readonly")  // 「false」と表示

//***** readAttributes() その1 (複数指定) *****
println Files.readAttributes(path, "dos:readonly,hidden")
  // 「[readonly:false, hidden:false]」と表示

//***** readAttributes() その2 (ワイルドカード) *****
println Files.readAttributes(path, "dos:*")
  // DosAttributeView のすべての属性(親クラス BasicAttributeView の属性を含む)
  // が読み込まれる

//***** setAttribute() *****
Files.setAttribute(path, "dos:readonly", true)  // 読み取り専用に設定

try {
    // ファイルに書き込んでみる
    Files.write(path, ["first line.", "second line"])
    assert false

}catch(AccessDeniedException ex){
    assert true // 例外が投げられれば OK
}
  • readAttributes() メソッドではワイルドカード (*) を用いて定義されている属性全てを選択することもできます。 このとき、親クラスが定義している属性も選択されます。
  • 接頭辞を付けなかった場合は、basic が指定されたもの、つまり BasicFileAttributeView の属性であると解釈されます。

この機能を使うと、いちいち java.nio.file.attribute パッケージのクラスを持ち出してこなくてもいいので楽ですね。 タイポしてもコンパイル前に教えてくれないけど。

【追記・修正】

  • 属性ビューの箇所に使用可能な属性の表を追加しました。
  • その他、ちらほら修正しました(コード含む)
  • 「文字列による属性の指定」を追記しました