Почему Java 7 Files.walkFileTree бросает исключение при столкновении с файлом tar на удаленном диске

Я использую Files.WalkFileTree() для навигации по папке и подсчета аудиофайлов, но возникает проблема, когда она встречает tar файл, кажется, что он рассматривает его как фактическую папку, которую я ожидал, чтобы просто пропустить ее.

Я не вижу никаких параметров, которые позволят мне контролировать это поведение

код:

package com.jthink.songkong.fileloader;


import com.jthink.songkong.cmdline.SongKong;
import com.jthink.songkong.ui.MainWindow;

import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.Callable;
import java.util.logging.Level;

/**
 * Count the number of files that can be loaded, for information purposes only
 */
public class CountFilesinFolder implements Callable<Boolean> {
    public static class CountFiles
            extends SimpleFileVisitor<Path> {
        private int fileCount = 0;
        private final PathMatcher matcher;

        CountFiles(String pattern) {
            matcher =
                    FileSystems.getDefault()
                            .getPathMatcher("regex:" + pattern);
        }

        /**
         * Find Music file
         *
         * @param file
         * @param attr
         * @return
         */
        @Override
        public FileVisitResult visitFile(Path file,
                                         BasicFileAttributes attr) {
            Path name = file.getFileName();
            if (name != null && matcher.matches(name)) {
                fileCount++;
            }
            return FileVisitResult.CONTINUE;
        }

        public int getFileCount() {
            return fileCount;
        }
    }


    private Path scanDir;
    public CountFilesinFolder(Path scanDir) {
        this.scanDir = scanDir;
    }

    public Boolean call() {
        CountFiles countFiles = null;
        try {
            countFiles = new CountFiles("^(?!._).*[.](?:mp3|mp4|m4p|m4b|m4a|ogg|flac|wma)$");
            Files.walkFileTree(scanDir, countFiles);
        }
        catch (Exception e) {
            MainWindow.logger.log(Level.SEVERE, "Unable to find file for deriving base folder", e);
        }
        MainWindow.logger.severe("Music File Count:"+countFiles.getFileCount());
        SongKong.setMaxProgress(countFiles.getFileCount());
        return true;
    }
}

дает этот стекtrace

java.nio.file.NoSuchFileException: Z:\Scratch\fred.tar
    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
    at sun.nio.fs.WindowsDirectoryStream.<init>(WindowsDirectoryStream.java:86)
    at sun.nio.fs.WindowsFileSystemProvider.newDirectoryStream(WindowsFileSystemProvider.java:526)
    at java.nio.file.Files.newDirectoryStream(Files.java:411)
    at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:179)
    at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:199)
    at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:69)
    at java.nio.file.Files.walkFileTree(Files.java:2591)
    at java.nio.file.Files.walkFileTree(Files.java:2624)
    at com.jthink.songkong.fileloader.CountFilesinFolder.call(CountFilesinFolder.java:68)
    at com.jthink.songkong.fileloader.CountFilesinFolder.call(CountFilesinFolder.java:15)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at java.lang.Thread.run(Thread.java:722)

но это удаленный диск (nas drive), я не вижу такой ошибки на локальном диске

ИЗМЕНИТЬ Реализовано следующее, основанное на ответе ниже. Я думал, работал

    @Override
            public FileVisitResult preVisitDirectory(Path dir, 
BasicFileAttributes attrs)
                    throws IOException {
                if(dir.endsWith(".tar"))
                {
                    return FileVisitResult.SKIP_SUBTREE;
                }
                return super.preVisitDirectory(dir, attrs);
            }

но мое тестирование было ошибочным, на самом деле это не работает, потому что код в FileTreeWalker, который не работает, вызывается перед использованием метода previsit

try {
            DirectoryStream<Path> stream = null;
            FileVisitResult result;

            // open the directory
            try {
                stream = Files.newDirectoryStream(file);
            } catch (IOException x) {
                return visitor.visitFileFailed(file, x);
            } catch (SecurityException x) {
                // ignore, as per spec
                return FileVisitResult.CONTINUE;
            }

            // the exception notified to the postVisitDirectory method
            IOException ioe = null;

            // invoke preVisitDirectory and then visit each entry
            try {
                result = visitor.preVisitDirectory(file, attrs);
                if (result != FileVisitResult.CONTINUE) {
                    return result;
                }

Ответы

Ответ 1

Временное решение проблемы:

Но реализуйте visitFileFailed, и вы должны быть в порядке.

public class MyFileVisitor extends SimpleFileVisitor<Path> {
    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
        if (file.toString().endsWith(".tar")) {
            return FileVisitResult.CONTINUE;
        }
        return super.visitFileFailed(file, exc);
    }
}

Обновление: Если мы посмотрим поближе, мы увидим, что walkFileTree использует Files.readAttributes, который переходит к текущему провайдеру в игре: WindowsFileSystemProvider.readAttributes, чтобы определить, является ли путь каталогом.

Как упоминалось в комментариях, я также не думаю, что ошибка в реализации Java, но OS-native-call, который возвращает неправильный атрибут

Если вы хотите сделать обходной путь для этого, одним из вариантов было бы реализовать свою собственную файловую систему, которая прозрачно переносит реализацию WindowsFileSystem, за исключением readAttributes возвращает .tar-пути как файл вместо dir.

Ответ 2

Тар-архивы можно рассматривать как каталог (набор файлов). Вы можете предотвратить эту ошибку, выполнив метод preVisitDirectory у вашего посетителя, просто вернув FileVisitResult.SKIP_SUBTREE для tar-архивов:

public static class CountFiles
        extends SimpleFileVisitor<Path> {
    ...
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
             throws IOException {
        if (dir.toString().toLowerCase().endsWith(".tar")) {
            return SKIP_SUBTREE;
        }
        return super.preVisitDirectory(dir, attrs);
    }
    ...
}