Java 7 walkFileTree在目录上调用visitFile

时间:2013-12-19 07:51:28

标签: java-7 nio2

说我有以下目录结构

/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)

3 个答案:

答案 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

调用walkFileTree
Files.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

调用walkFileTree
Files.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参数检查文件类型。