Aks*_*ert 21 java nio java-8 java-stream
我正在尝试计算光盘上文件的大小.在java-7中,这可以使用Files.walkFileTree来完成,如我在这里的回答所示.
但是,如果我想使用java-8流来执行此操作,它将适用于某些文件夹,但不适用于所有文件夹.
public static void main(String[] args) throws IOException {
long size = Files.walk(Paths.get("c:/")).mapToLong(MyMain::count).sum();
System.out.println("size=" + size);
}
static long count(Path path) {
try {
return Files.size(path);
} catch (IOException | UncheckedIOException e) {
return 0;
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码适用于路径,a:/files/但c:/它会抛出异常
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException: c:\$Recycle.Bin\S-1-5-20
at java.nio.file.FileTreeIterator.fetchNextIfNeeded(Unknown Source)
at java.nio.file.FileTreeIterator.hasNext(Unknown Source)
at java.util.Iterator.forEachRemaining(Unknown Source)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.LongPipeline.reduce(Unknown Source)
at java.util.stream.LongPipeline.sum(Unknown Source)
at MyMain.main(MyMain.java:16)
Run Code Online (Sandbox Code Playgroud)
我了解它的来源以及如何使用Files.walkFileTree API来避免它.
但是如何使用Files.walk() API 避免这种异常呢?
ski*_*iwi 25
不,这个例外是无法避免的.
异常本身发生在lazy fetch中Files.walk(),因此为什么你没有及早看到它以及为什么没有办法绕过它,请考虑以下代码:
long size = Files.walk(Paths.get("C://"))
.peek(System.out::println)
.mapToLong(this::count)
.sum();
Run Code Online (Sandbox Code Playgroud)
在我的系统上,这将在我的计算机上打印:
C:\
C:\$Recycle.Bin
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException: C:\$Recycle.Bin\S-1-5-18
Run Code Online (Sandbox Code Playgroud)
并且当在第三个文件的(主)线程上抛出异常时,该线程上的所有进一步执行都会停止.
我相信这是一个设计失败,因为它现在Files.walk已经绝对无法使用,因为你无法保证在目标上行走时不会出现错误.
需要注意的一点是,堆栈跟踪包括一个sum()和reduce()操作,这是因为路径被延迟加载,所以在这一点上reduce(),大量的流机制被调用(在堆栈跟踪中可见),然后它获取路径,在哪一点UnCheckedIOException发生.
如果让每个步行操作都在自己的线程上执行,则可能会被绕过.但这不是你想要做的事情.
此外,检查文件是否实际可访问是没有价值的(尽管在某种程度上有用),因为您无法保证即使在1ms之后它也是可读的.
我相信它仍然可以修复,但我不知道究竟FileVisitOption是如何工作的.
目前有一个FileVisitOption.FOLLOW_LINKS,如果它在每个文件的基础上运行,那么我怀疑FileVisitOption.IGNORE_ON_IOEXCEPTION也可以添加,但是我们无法在那里正确地注入该功能.
Abh*_*ari 16
2017年为那些一直到这里的人.
当您确定文件系统行为时,请使用Files.walk(),并且在发生任何错误时确实要停止.通常,Files.walk在独立应用程序中没用.我经常犯这个错误,也许我很懒.我意识到自己的错误,当我看到时间持续超过几秒钟,就像一百万个小文件一样.
我推荐walkFileTree.首先实现FileVisitor接口,这里我只想计算文件.我知道坏名字.
class Recurse implements FileVisitor<Path>{
private long filesCount;
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
//This is where I need my logic
filesCount++;
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// This is important to note. Test this behaviour
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
public long getFilesCount() {
return filesCount;
}
}
Run Code Online (Sandbox Code Playgroud)
然后像这样使用你定义的类.
Recurse r = new Recurse();
Files.walkFileTree(Paths.get("G:"), r);
System.out.println("Total files: " + r.getFilesCount());
Run Code Online (Sandbox Code Playgroud)
我相信你知道如何修改你自己的类的实现FileVisitor<Path>Interface类来做其他事情filesize,比如我发布的例子.有关此处的其他方法,请参阅文档
速度:
编辑:与所有内容一样,使用测试来确认行为处理异常,它们仍然会发生,除了我们选择不关注的那些.
我发现使用Guava的Files类为我解决了这个问题:
Iterable<File> files = Files.fileTreeTraverser().breadthFirstTraversal(dir);
long size = toStream( files ).mapToLong( File::length ).sum();
Run Code Online (Sandbox Code Playgroud)
toStream我的静态实用程序函数将Iterable转换为Stream的位置在哪里。只是这个:
StreamSupport.stream(iterable.spliterator(), false);
Run Code Online (Sandbox Code Playgroud)