避免 Java 8 中 Files.walk(..) 的终止,因为会引起 java.nio.file.AccessDeniedException。

8
我正在使用Java 8Files.walk(..)来计算一个文件夹及其所有子文件夹中包含的.mp3文件数量。换句话说,我正在访问文件树的所有级别。
当我遇到java.nio.file.AccessDeniedException时,Stream会关闭,但我不希望出现这种情况。我需要它忽略或打印异常并继续计数文件。以下是我使用的代码 :):
   /**
     * Count files in a directory (including files in all sub
     * directories)
     * 
     * @param directory
     *        the directory to start in
     * @return the total number of files
     */
    public int countFiles(File dir) {
        if (dir.exists())
            try (Stream<Path> paths = Files.walk(Paths.get(dir.getPath()), FileVisitOption.FOLLOW_LINKS)) {
                return (int) paths.filter(path -> {

                    // i am using something different here but i changed
                    // it just for the purpose of StackOverFlow question                                
                    return path.toString().contains(".mp3");

                }).count();
            } catch (IOException ex) {
                //Main.logger.log(Level.WARNING, "", ex);
                ex.printStackTrace();
            }

        return 0;
    }

错误的堆栈跟踪:
java.io.UncheckedIOException: java.nio.file.AccessDeniedException: C:\$Recycle.B
in\S-1-5-18
    at java.nio.file.FileTreeIterator.fetchNextIfNeeded(FileTreeIterator.java:88)
    at java.nio.file.FileTreeIterator.hasNext(FileTreeIterator.java:104)
    at java.util.Iterator.forEachRemaining(Iterator.java:115)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.jav
a:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
    at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
    at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
    at smartcontroller.SmartController$InputService$1.countFiles(SmartController.ja
va:2092)
...

类似的问题,尽管不完全相同,我需要返回一个流。

Java7中绕过文件遍历树中的访问被拒绝问题



@JarrodRoberson 我需要获取一个 Stream,这样我就可以使用Java8的方法。你能发一篇关于它的答案吗 :) ? - GOXR3PLUS
你的代码哪一行出错了?错误是来自于 filter 内部的操作吗?还是来自于 Files.walk - 4castle
@4castle 我的错误,不过我已经通过编辑修复了它 :) 这个错误来自于 File.walk(..) - GOXR3PLUS
2
@JarrodRoberson 你好,Jarrod :),我读了您发布的重复链接。虽然这不是我正在寻找的,但异常是来自try(Stream <Path> paths = Files.walk(Paths.get(dir.getPath(),FileVisitOption.FOLLOW_LINKS))),因此无论我做什么都会抛出异常。我需要在内部修复Files.walk(...)。 那么它可能是重复的,但不是您标记的那种重复...如果您有任何想法,请将其发布为答案。 - GOXR3PLUS
1
我同意这与链接重复内容无关。重新开启。 - shmosel
显示剩余7条评论
1个回答

5

答案

这里是一个临时解决方案,可以改进为使用Java 8 Streams和Lambdas。

int[] count = {0};
try {
    Files.walkFileTree(
            Paths.get(dir.getPath()), 
            new HashSet<FileVisitOption>(Arrays.asList(FileVisitOption.FOLLOW_LINKS)),
            Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
                        throws IOException {
                    System.out.printf("Visiting file %s\n", file);
                    ++count[0];
                    return FileVisitResult.CONTINUE;
                }
                
                @Override
                public FileVisitResult visitFileFailed(Path file, IOException e) 
                        throws IOException {
                    System.err.printf("Visiting failed for %s\n", file);
                    return FileVisitResult.SKIP_SUBTREE;
                }
                
                @Override
                public FileVisitResult preVisitDirectory(Path dir,
                                                         BasicFileAttributes attrs) 
                        throws IOException {
                    System.out.printf("About to visit directory %s\n", dir);
                    return FileVisitResult.CONTINUE;
                }
            });
} catch (IOException e) {
    // handle exception
}

只是为了澄清,从visitFileFailed()返回SKIP_SUBTREE没有意义。它只有在从preVisitDirectory()返回时才有意义。 - ne1410s
所以基本上,如果无法访问文件夹,FileVisitResult.SKIP_SUBTREE 就不起作用,我们应该将其放在 preVisitDirectory 上吗? - GOXR3PLUS
1
是的 - 至少是后半部分 :) 问题在于跳过目录的能力只能在 preVisitDirectory() 中决定。试图从文件故障中跳过目录的行为与继续相同。 - ne1410s
谢谢,我会看看的。当前的代码奇怪地工作了,让我很困惑 :) - GOXR3PLUS
不必创建一个 FileVisitOptions 数组,将其转换为列表,然后再转换为集合,你可以像这样更简单地编写它:EnumSet.of(FileVisitOption.FOLLOW_LINKS) - Hoov
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接