java7:Files.walkFileTree()和"系统卷信息"在Windows系统上

时间:2015-03-01 13:10:06

标签: java windows nio ntfs

使用根文件夹(例如" T:/")提供Files.walkFileTree()会产生错误:

 java.nio.file.AccessDeniedException: T:\System Volume Information
    at java.nio.file.Files.newDirectoryStream(Files.java:457)
    at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:300)
    at java.nio.file.FileTreeWalker.next(FileTreeWalker.java:372)
    at java.nio.file.Files.walkFileTree(Files.java:2706)
    at java.nio.file.Files.walkFileTree(Files.java:2742)

不调用回调preVisitDirectory():

            @Override
        public FileVisitResult preVisitDirectory( Path aFile,
                                                  BasicFileAttributes aAttrs )
            throws IOException
        {
            if ( "System Volume Information".equals( ( aFile.getFileName() ) ) )
            {
                return FileVisitResult.SKIP_SUBTREE;
            }

            return FileVisitResult.CONTINUE;
        }

apache FileUtils.deleteDirectory(新文件(" T:/"))都无法处理这种情况。

我遇到了以下代码:

    public static void deleteDirRecursive( Path aDir ) throws IOException
{
    if ( aDir == null )
    {
        throw new IllegalArgumentException( "aDir must not be null" );
    }

    if ( Files.notExists( aDir ) )
    {
        return;
    }

    if ( aDir.isAbsolute() && aDir.getRoot().equals( aDir ) )
    {
        myLog.debug( "Given path object is a root. On windows we cannot delete 'System Volume Information' folder!" );

        // -> iterate over the entries in root and skip "System Volume information"
        Arrays.asList( aDir.toFile().listFiles( 
            new FilenameFilter() 
            {
                @Override
                public boolean accept( File aDirectory, String aName )
                {
                    return !"System Volume Information".equals( aName );
                }
            } )
        ).forEach
            ( dir -> 
                {
                    try
                    {
                        if ( dir.isDirectory() )
                        {
                            deleteDirRecursive( Paths.get( dir.getAbsolutePath() ) );
                        }
                        else
                        {
                            Files.delete( Paths.get( dir.getAbsolutePath() ) );

                        }
                    }
                    catch ( Exception e )
                    {
                        throw new IllegalArgumentException( "", e );
                    }
                }
            );

        return;
    }

    if ( !Files.isDirectory( aDir ) )
    {
        throw new IllegalArgumentException( "given aDir is not a directory" );
    }


    Files.walkFileTree( aDir, new SimpleFileVisitor<Path>()
    {
        @Override
        public FileVisitResult visitFile( Path file,
                                          BasicFileAttributes attrs )
            throws IOException
        {
            Files.delete( file );
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory( Path dir, IOException exc )
            throws IOException
        {
            Files.delete( dir );
            return FileVisitResult.CONTINUE;
        }

    } );
}

请注意,此代码的最大部分仅是对#34;系统卷信息&#34;的特殊处理。 (整个&#34; if(aDir.isAbsolute()....&#34;)。非常难看。

是否有更优雅的解决方案可以从树木漫步中排除此文件夹?

2 个答案:

答案 0 :(得分:2)

我知道现在已经很晚了,但我也有这个问题;我会在那里记录。

FileVisitor有一个名为FileVisitResult visitFileFailed(T file, IOException exc)

的方法

SimpleFileVisitor通过重新抛出异常来实现它。

只需在您自己的实现中覆盖该方法,以您认为必要的方式处理异常。

答案 1 :(得分:0)

以下是我的一个名为java7-fs-more的软件包的解决方案:

final Path path = Paths.get("T:");

MoreFiles.deleteRecursive(path, RecursionMode.KEEP_GOING);

这将继续删除文件和目录,即使它在某一点失败。

回来后,你会得到(在你的情况下)RecursiveDeletionException;删除期间引发的所有异常都“附加”到此异常(请参阅Throwable#getSuppressed())。

另一种解决方案(不保证)是检查路径的dos:system属性;如果为true,那么您可能希望避免删除它。

请注意,在FileVisitor中,您很遗憾只能在访问文件时访问BasicFileAttributes;例如,要检查dos:system,您要执行的操作是:

Files.readAttributes(path, DosFileAttributes.class).isSystem()

另一个解决方案,因为您似乎使用Java 8,是使用Files.walk()并过滤条目;不幸的是,读取属性将引发IOException,所以,再次,我使用我的另一个包throwing-lambdas,以某种方式解决它:

final ThrowingPredicate<Path> notSystem
    = path -> !Files.readAttributes(path, DosFileAttributes.class).isSystem();

final ThrowingConsumer<Path> remove = Files::delete;

try (
    final Stream<Path> stream = Files.walk(Paths.get("T:"));
) {
    stream.filter(notSystem.orReturnTrue()).forEach(remove.orDoNothing());
}