FileVisitor没有按预期工作

huangapple go评论72阅读模式
英文:

FileVisitor not behaving as expected

问题

我一直在尝试使用实现了FileVisitor接口的类,该类在preVisitDirectory中简单地复制目录,在postVisitDirectory中移动文件并删除目录。这在没有任何问题的情况下运行。

问题出在我尝试实现一个条件语句时:if(Files.list(dir).findAny().isEmpty())

除了最浅层的子目录外,其余的子目录仍然存在于原始目录中。目标目录被完全正确地填充。没有抛出任何错误。

如果我在条件语句的流中使用skip(1),会抛出DirectoryNotEmptyException异常,在删除行上会出现异常。功能是不正确的,即使我没有使用skip,结果与使用条件语句时一样。

如果我使用Files.list(dir).forEach(System.out::println);进行打印输出,功能是不正确的,并且会再次在删除行上抛出异常。

另一个人认为功能不如预期,是因为这个:

System.out.println(dir + " : ");
Files.list(dir).forEach(System.out::println);

输出:

> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder\TriplyNestedFolder:
> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder: 
> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder\TriplyNestedFolder
> E:\EncrypterTest\TopSecret\NestedFolder : 
> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder

其中TriplyNestedFolder是目录中最深的文件夹。请记住,当代码完成时,TriplyNestedFolder将被删除。如果我不检查它们是否为空,其他两个文件夹也会被删除。

因此,总结起来似乎是:

  1. 首先访问最深的目录(预期行为),但尽管在其上调用了.delete,它仍然存在于其父目录中,以便进行删除。
  2. 在代码完成后被删除
  3. 所有父目录也都被删除,没有抛出异常
  4. 添加如上所示的条件语句会导致除最深的目录外的所有副本保留
  5. 在文件流中添加.skip(1)会导致来自第4点的功能,但会产生DirectoryNotEmptyException异常
  6. 添加打印输出会导致与第5点相同的功能和异常
  7. 无论目录中是否有文件,行为都完全相同

我希望对于这里的某个人来说,这将是一些明显的事情,但是我束手无策。提前感谢您提供的任何帮助。如果您需要更多的信息/代码,请随时提问。

英文:

I have been trying to use a class that implements FileVisitor and simply copies a directory in preVisitDirectory, moves the files, and deletes the directory in `postVisitDirectory. This works without any problems.

The problem occurs when I try to implement a conditional: if(Files.list(dir).findAny().isEmpty())

All but the shallowest subdirectories persist in the original directory. The target directory is filled out 100% correctly. No error is thrown.

If I use skip(1) in the stream of the conditional, a DirectoryNotEmptyException is thrown. *on the delete line.*Functionality is incorrect, i.e., the same as if I used the conditional without skip.

If I do a printout with Files.list(dir).forEach(System.out::println);, functionality is incorrect and the exception is thrown, again on the delete line

Another human thinks functionality was not as intended because this:

'System.out.println(dir + " : ");
Files.list(dir).forEach(System.out::println);'

Outputs:

> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder\TriplyNestedFolder:
> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder:
> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder\TriplyNestedFolder
> E:\EncrypterTest\TopSecret\NestedFolder :
> E:\EncrypterTest\TopSecret\NestedFolder\DoublyNestedFolder

Where TriplyNestedFolder is the deepest folder in the directory. Remember, TriplyNestedFolder is being deleted when the code is complete. So are the other 2 if I don't check if they are empty.

So to recap it seems like:

  1. The deepest directory is being accessed first (as expected), but despite .delete being called on it, it still exists for the purpose of the .delete in it's parent directory.
  2. It is deleted after code completes
  3. Parent directories are all deleted as well with no exception thrown
  4. Adding a conditional as shown above causes copies of all but the deepest directory to remain
  5. Adding a .skip(1) in the files stream causes functionality from #4, but produces DirectoryNotEmptyException
  6. Adding a printout causes the same functionality and exception from #5
  7. Exact same behavior occurs with or without files in the directories

I'm hoping this will be something obvious to someone here, but I'm at a loss. Thanks in advance for any help. If you need any more information/code please ask.

答案1

得分: 0

Javadoc Files.list(Path dir) 的说明如下:

> 返回一个惰性地填充的 Stream,其中的元素是目录中的条目。[...]
>
> [...]
>
> 返回的流封装了一个 DirectoryStream。如果需要及时释放文件系统资源,应使用try-with-resources结构,以确保在完成流操作后调用流的 close 方法。
>
> [...]

这意味着返回的流保持着一个打开的操作系统目录句柄,这在Windows上可能会阻止删除目录。

Files.list() 的正确使用方式如下:

boolean isEmpty;
try (Stream<Path> pathStream = Files.list(dir)) {
    isEmpty = pathStream.findAny().isEmpty();
}
if (isEmpty) {
    // 我们现在可以删除目录,因为我们已经释放了流使用的资源,即我们已经关闭了句柄
}
英文:

Javadoc of Files.list(Path dir) says:

> Return a lazily populated Stream, the elements of which are the entries in the directory. [...]
>
> [...]
>
> The returned stream encapsulates a DirectoryStream. If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.
>
> [...]

This means that the returned stream keeps an open OS directory handle, which on Windows may prevent the directory from being deleted.

Correct use of Files.list() is:

boolean isEmpty;
try (Stream<Path> pathStream = Files.list(dir)) {
    isEmpty = pathStream.findAny().isEmpty();
}
if (isEmpty) {
    // We can delete the directory here, since we've now released
    // the resources used by the stream, i.e. we've closed the handle
}

huangapple
  • 本文由 发表于 2020年9月6日 16:42:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/63762289.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定