说我有以下目录结构
/root/dir
/root/dir/file1.txt
/root/dir/subdir
/root/dir/subdir/file2.txt
我们假设我将使用以下访问者:
class MyFileVisitor extends SimpleFileVisitor<Path> {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs)
throws IOException {
if(Files.isDirectory(file)) {
throw new IllegalStateException("WAT!? Visiting directory: "+file.toAbsolutePath().toString());
}
System.out.println("Visiting file: "+file.toAbsolutePath().toString());
return super.visitFile(file, attrs);
}
如果我们使用walkFileTree的简单重载:
Files.walkFileTree(Paths.get("/root/dir"), new MyFileVisitor());
一切都按计划进行,我们看到以下输出:
Visiting file: /root/dir/file1.txt
Visiting file: /root/dir/subdir/file2.txt
但是当我们尝试设置最大深度时,事情开始崩溃:
Files.walkFileTree(Paths.get("/root/dir"), EnumSet.noneOf(FileVisitOption.class), 1, new MyFileVisitor());
输出:
Visiting file: /root/dir/file1.txt
java.lang.IllegalStateException: WAT!? Visting directory: /root/dir/subdir
我很确定这是一个错误,但我想首先询问社区:是否有我遗漏的东西,这实际上是预期的行为?谢谢你确认!
详细说明:
java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
答案 0 :(得分:4)
我用你的代码玩了一下,并添加了几行:
/*
* (non-Javadoc)
*
* @see java.nio.file.SimpleFileVisitor#preVisitDirectory(java.lang.Object,
* java.nio.file.attribute.BasicFileAttributes)
*/
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
System.out.println(Thread.currentThread().getStackTrace()[1] + " "
+ dir);
return super.preVisitDirectory(dir, attrs);
}
/*
* (non-Javadoc)
*
* @see java.nio.file.SimpleFileVisitor#postVisitDirectory(java.lang.Object,
* java.io.IOException)
*/
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
System.out.println(Thread.currentThread().getStackTrace()[1] + " "
+ dir);
return super.postVisitDirectory(dir, exc);
}
/*
* (non-Javadoc)
*
* @see java.nio.file.SimpleFileVisitor#visitFileFailed(java.lang.Object,
* java.io.IOException)
*/
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
System.out.println(Thread.currentThread().getStackTrace()[1] + " "
+ file);
return super.visitFileFailed(file, exc);
}
只是毫无疑问地看看发生了什么。
你所谓的错误发生在java.nio.file.FileTreeWalker:134
:
// at maximum depth or file is not a directory
if (depth >= maxDepth || !attrs.isDirectory()) {
return visitor.visitFile(file, attrs);
}
1:subdir是深度1的一部分
2:没有visitFile
最深的目录,你甚至不会认识到它们在那里。
所以我认为这是常规行为。
顺便说一句,您可以在覆盖:
中使用attrs@Override
public FileVisitResult visitFile(final Path file,
final BasicFileAttributes attrs) throws IOException {
if (attrs.isDirectory()) {
throw new IllegalStateException("WAT!? Visiting directory: "
+ file.toAbsolutePath().toString());
}
System.out
.println("Visiting file: " + file.toAbsolutePath().toString());
return super.visitFile(file, attrs);
}
答案 1 :(得分:2)
老实说,我相信java.nio.file.Files#walkFileTree(java.nio.file.Path, java.util.Set, int, java.nio.file.FileVisitor)违反了java.nio.file.FileVisitor界面的约定。
简而言之:如果目录恰好在所提供的深度级别上,则也会在目录上调用visitFile方法 。
如果您阅读接口java.nio.file.FileVisitor#visitFile的文档,其中没有明确指出:
调用目录中的文件。
示例:
给出以下文件系统结构:
───start_dir
│ file1.txt
│
├───dir1
│ file11.txt
│
└───dir2
└───dir21
file211.txt
调用walkFileTree可以按预期的方式进行,足够大的深度
Files.walkFileTree(Paths.get("start_dir"), EnumSet.noneOf(FileVisitOption.class),1000, new MyFileVisitor());
产生与接口一致的以下调用:
Previsit dir: start_dir
Previsit dir: start_dir\dir1
Visit file: start_dir\dir1\file11.txt
Postvisit dir: start_dir\dir1
Previsit dir: start_dir\dir2
Previsit dir: start_dir\dir2\dir21
Visit file: start_dir\dir2\dir21\file211.txt
Postvisit dir: start_dir\dir2\dir21
Postvisit dir: start_dir\dir2
Visit file: start_dir\file1.txt
Postvisit dir: start_dir
以深度1
调用walkFileTreeFiles.walkFileTree(Paths.get("start_dir"), EnumSet.noneOf(FileVisitOption.class),1, new MyFileVisitor());
产生以下调用,在这些调用中,除了文件之外,还会在第一级目录上调用visitFile:
Previsit dir: start_dir
Visit file: start_dir\dir1
Visit file: start_dir\dir2
Visit file: start_dir\file1.txt
Postvisit dir: start_dir
以深度2
调用walkFileTreeFiles.walkFileTree(Paths.get("start_dir"), EnumSet.noneOf(FileVisitOption.class),2, new MyFileVisitor());
产生以下调用,其中在第一级目录上不调用,而不在第二级目录上调用visitFile:
Previsit dir: start_dir
Previsit dir: start_dir\dir1
Visit file: start_dir\dir1\file11.txt
Postvisit dir: start_dir\dir1
Previsit dir: start_dir\dir2
Visit file: start_dir\dir2\dir21
Postvisit dir: start_dir\dir2
Visit file: start_dir\file1.txt
Postvisit dir: start_dir
答案 2 :(得分:0)
此行为是在documentation of Files::walkFileTree中指定的,因此它是故意的,而不是错误:
在maxDepth遇到的所有文件(包括目录)都将调用visitFile方法
为捍卫在目录上调用visitFile
,在java.nio.file
类的整个文档中,目录都隐式称为文件。例如,Files::walkFileTree
本身的文档说:“此方法遍历以给定起始文件为根的文件树”,并且“起始 file ”(当然)可以是< strong>目录。
还要注意,visitFileFailed
也可以在目录上调用,如文档中所述:
如果文件是目录,并且无法打开目录, 然后使用I / O异常调用visitFileFailed方法
Franz Ebner显示了发生此行为的代码,我同意他认为这是故意的原因:“如果没有用于最深目录的visitFile,您甚至都不会意识到它们在那里。”如果只需要常规文件(或符号链接),我还同意使用attrs
参数检查文件类型。