#環境
##OS
Windows7 64bit
##Java
1.7.0_51
#基本
フォルダ階層
nio
│ file1.txt
│
└─dir1
│ file2.txt
│
└─dir2
file3.txt
MyFileVisitor.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import org.apache.commons.lang3.text.StrBuilder;
public class MyFileVisitor implements FileVisitor<Path> {
protected int indentSize;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
print("preVisitDirectory : " + dir.getFileName());
this.indentSize++;
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
print("visitFile : " + file.getFileName());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
this.indentSize--;
print("postVisitDirectory : " + dir.getFileName());
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
String error = String.format(" [exception=%s, message=%s]", exc.getClass(), exc.getMessage());
print("visitFileFailed : " + file.getFileName() + error);
return FileVisitResult.CONTINUE;
}
protected void print(String message) {
System.out.println(new StrBuilder().appendPadding(this.indentSize, ' ').append(message));
}
}
Main.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
FileVisitor<Path> visitor = new MyFileVisitor();
Files.walkFileTree(start, visitor);
}
}
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : dir2
visitFile : file3.txt
postVisitDirectory : dir2
visitFile : file2.txt
postVisitDirectory : dir1
visitFile : file1.txt
postVisitDirectory : nio
-
Files.walkFileTree(Path, FileVisitor)
で指定したフォルダ以下を再帰的に処理できる。 - 各フォルダの検索前後で
preVisitDirectory()
とpostVisitDirectory()
がコールバックされる。 - 各ファイルごとに
visitFile()
がコールバックされる。
#フォルダのリンクがあった場合の処理
フォルダ階層
│ file1.txt
│
├─dir1
│ │ file2.txt
│ │
│ └─dir2
│ file3.txt
│
├─junction_dir2
│ file3.txt
│
└─symbolic_dir2
file3.txt
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : dir2
visitFile : file3.txt
postVisitDirectory : dir2
visitFile : file2.txt
postVisitDirectory : dir1
visitFile : file1.txt
preVisitDirectory : junction_dir2
visitFile : file3.txt
postVisitDirectory : junction_dir2
visitFile : symbolic_dir2
postVisitDirectory : nio
-
junction_dir2
がdir1\dir2
のジャンクション、symbolic_dir2
がシンボリックリンクになっている。 - ジャンクションは再帰対象になるが、シンボリックリンクは対象にならない。
#シンボリックリンクのフォルダも再帰対象にする
フォルダ階層
│ file1.txt
│
├─dir1
│ │ file2.txt
│ │
│ └─dir2
│ file3.txt
│
├─junction_dir2
│ file3.txt
│
└─symbolic_dir2
file3.txt
Main.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Set;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
FileVisitor<Path> visitor = new MyFileVisitor();
Set<FileVisitOption> options = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
int maxDepth = Integer.MAX_VALUE;
Files.walkFileTree(start, options, maxDepth, visitor);
}
}
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : dir2
visitFile : file3.txt
postVisitDirectory : dir2
visitFile : file2.txt
postVisitDirectory : dir1
visitFile : file1.txt
preVisitDirectory : junction_dir2
visitFile : file3.txt
postVisitDirectory : junction_dir2
preVisitDirectory : symbolic_dir2
visitFile : file3.txt
postVisitDirectory : symbolic_dir2
postVisitDirectory : nio
-
walkFileTree(Path, Set<FileVisitOption>, int, FileVisitor)
メソッドを使い、Set<FileVisitOption>
でFileVisitOption.FOLLOW_LINKS
を渡すと、シンボリックリンクのフォルダも再帰処理の対象になる。
#再帰の深さを指定する
フォルダ階層
└─dir1
└─dir2
└─dir3
Main.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.EnumSet;
import java.util.Set;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
Set<FileVisitOption> options = EnumSet.allOf(FileVisitOption.class);
int maxDepth = 2; // 深さ 2 を指定
FileVisitor<Path> visitor = new MyFileVisitor();
Files.walkFileTree(start, options, maxDepth, visitor);
}
}
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
visitFile : dir2
postVisitDirectory : dir1
postVisitDirectory : nio
-
maxDepth
で再帰の深さを指定できる。 -
maxDepth
で指定した深さにあるフォルダは、visitFile()
で処理される。
#リンクのフォルダを辿った結果、ループが検出された場合の処理
フォルダ階層
│ file1.txt
│
└─dir1
│ file2.txt
│
└─dir2
│ file3.txt
│
└─link_dir1
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : dir2
visitFile : file3.txt
visitFileFailed : link_dir1 [exception=class java.nio.file.FileSystemLoopException, message=nio\dir1\dir2\link_dir1]
postVisitDirectory : dir2
visitFile : file2.txt
postVisitDirectory : dir1
visitFile : file1.txt
postVisitDirectory : nio
-
link_dir1
が、dir1
のジャンクションになっており、再帰処理をすると無限ループが発生するようになっている。 - 再帰処理でループが検出された場合、
visitFileFailed()
がコールバックされる。 - このとき、
FileSystemLoopException
がvisitFileFailed()
メソッドに渡される。
#任意のタイミングで再帰検索を中断する
フォルダ階層
└─dir1
├─dir2
└─terminate
TerminateFileVisitor.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public class TerminateFileVisitor extends MyFileVisitor {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
print("preVisitDirectory : " + dir.getFileName());
this.indentSize++;
if ("terminate".equals(dir.getFileName().toString())) {
System.out.println("TERMINATE!!");
return FileVisitResult.TERMINATE;
} else {
return FileVisitResult.CONTINUE;
}
}
}
Main.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
FileVisitor<Path> visitor = new TerminateFileVisitor();
Files.walkFileTree(start, visitor);
}
}
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : dir2
postVisitDirectory : dir2
preVisitDirectory : terminate
TERMINATE!!
-
FileVisitor
のメソッドがFileVisitResult.TERMINATE
を返すと、その時点で再帰処理は中断される。
#特定のフォルダだけ再帰処理をしないようにする
フォルダ階層
├─dir1
│ └─dir2
└─skip
└─dir3
SkipSubtreeFileVisitor.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public class SkipSubtreeFileVisitor extends MyFileVisitor {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
print("preVisitDirectory : " + dir.getFileName());
this.indentSize++;
if ("skip".equals(dir.getFileName().toString())) {
System.out.println("SKIP!!");
this.indentSize--;
return FileVisitResult.SKIP_SUBTREE;
} else {
return FileVisitResult.CONTINUE;
}
}
}
Main.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
FileVisitor<Path> visitor = new SkipSubtreeFileVisitor();
Files.walkFileTree(start, visitor);
}
}
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : dir2
postVisitDirectory : dir2
postVisitDirectory : dir1
preVisitDirectory : skip
SKIP!!
postVisitDirectory : nio
-
preVisitDirectory()
メソッドがFileVisitResult.SKIP_SUBTREE
を返した場合、そのフォルダはスキップされる(postVisitDirectory()
メソッドも呼ばれない)。
#任意のタイミングで、特定のフォルダ以下の処理だけを中断させる
フォルダ階層
├─dir1
│ │ bbb_skip.txt
│ │
│ ├─aaa
│ └─ccc
└─dir2
SkipSiblingsFileVisitor.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
public class SkipSiblingsFileVisitor extends MyFileVisitor {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
print("visitFile : " + file.getFileName());
if (file.getFileName().toString().contains("skip")) {
System.out.println("SKIP!!");
return FileVisitResult.SKIP_SIBLINGS;
} else {
return FileVisitResult.CONTINUE;
}
}
}
Main.java
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
FileVisitor<Path> visitor = new SkipSiblingsFileVisitor();
Files.walkFileTree(start, visitor);
}
}
実行結果
preVisitDirectory : nio
preVisitDirectory : dir1
preVisitDirectory : aaa
postVisitDirectory : aaa
visitFile : bbb_skip.txt
SKIP!!
postVisitDirectory : dir1
preVisitDirectory : dir2
postVisitDirectory : dir2
postVisitDirectory : nio
-
FileVisitResult.SKIP_SIBLINGS
を返すと、現在処理中のフォルダに存在する未処理のファイルとフォルダは全て処理をスキップする。
#SimpleFileVisitor を使って必要な処理だけ実装する
SimpleFileVisitor
は FileVisitor
インターフェースの実装クラスで、 FileVisitor
の各メソッドのデフォルト実装を提供している。
FileVisitor
の全てのメソッドを実装する必要がない場合、 SimpleFileVisitor
を継承して必要なメソッドだけオーバーライドする。
package sample.nio;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class Main {
public static void main(String[] args) throws IOException {
Path start = Paths.get("nio");
Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
System.out.println(file.getFileName());
return FileVisitResult.CONTINUE;
}
});
}
}
#参考