Directory.EnumerateFiles => UnauthorizedAccessException

Ben*_*sen 37 .net filesystems lazy-evaluation unauthorizedaccessexcepti

.NET 4.0中有一个很好的新方法,可以通过枚举以流方式获取目录中的文件.

这里的问题是,如果想要枚举所有文件,可能事先不知道哪些文件或文件夹受到访问保护并且可能抛出UnauthorizedAccessException.

要重现,可以运行此片段:

foreach (var file in Directory.EnumerateFiles(@"c:\", "*", SearchOption.AllDirectories))
{
   // whatever
}
Run Code Online (Sandbox Code Playgroud)

在此.NET方法存在之前,通过在字符串数组返回方法上实现递归迭代器,可以实现大致相同的效果.但它并不像新的.NET方法那么懒惰.

那么该怎么办?使用此方法时,UnauthorizedAccessException可以被抑制还是生活中的事实?

在我看来,该方法应该有一个重载接受一个动作来处理任何异常.

str*_*dso 29

我无法完成上述工作,但这是我的实现,我已经在"Win7"盒子上的c:\ users上进行了测试,因为如果有所有这些"令人讨厌"的目录:

SafeWalk.EnumerateFiles(@"C:\users", "*.jpg", SearchOption.AllDirectories).Take(10)
Run Code Online (Sandbox Code Playgroud)

类:

public static class SafeWalk
{
    public static IEnumerable<string> EnumerateFiles(string path, string searchPattern, SearchOption searchOpt)
    {   
        try
        {
            var dirFiles = Enumerable.Empty<string>();
            if(searchOpt == SearchOption.AllDirectories)
            {
                dirFiles = Directory.EnumerateDirectories(path)
                                    .SelectMany(x => EnumerateFiles(x, searchPattern, searchOpt));
            }
            return dirFiles.Concat(Directory.EnumerateFiles(path, searchPattern));
        }
        catch(UnauthorizedAccessException ex)
        {
            return Enumerable.Empty<string>();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果遇到受限制的文件并抛出/忽略异常会发生什么.它不会只是停在那里而忽略这一点背后的所有文件吗? (5认同)

小智 8

上述答案的问题是在子目录中没有处理异常.这将是处理这些异常的更好方法,因此您可以从所有子目录获取所有文件,除了那些引发访问异常的文件:

    /// <summary>
    /// A safe way to get all the files in a directory and sub directory without crashing on UnauthorizedException or PathTooLongException
    /// </summary>
    /// <param name="rootPath">Starting directory</param>
    /// <param name="patternMatch">Filename pattern match</param>
    /// <param name="searchOption">Search subdirectories or only top level directory for files</param>
    /// <returns>List of files</returns>
    public static IEnumerable<string> GetDirectoryFiles(string rootPath, string patternMatch, SearchOption searchOption)
    {
        var foundFiles = Enumerable.Empty<string>();

        if (searchOption == SearchOption.AllDirectories)
        {
            try
            {
                IEnumerable<string> subDirs = Directory.EnumerateDirectories(rootPath);
                foreach (string dir in subDirs)
                {
                    foundFiles = foundFiles.Concat(GetDirectoryFiles(dir, patternMatch, searchOption)); // Add files in subdirectories recursively to the list
                }
            }
            catch (UnauthorizedAccessException) { }
            catch (PathTooLongException) {}
        }

        try
        {
            foundFiles = foundFiles.Concat(Directory.EnumerateFiles(rootPath, patternMatch)); // Add files from the current directory
        }
        catch (UnauthorizedAccessException) { }

        return foundFiles;
    }
Run Code Online (Sandbox Code Playgroud)

  • 请不要引用“上述答案”的其他答案... (2认同)

Dan*_*mov 5

我知道这MoveNext是抛出异常。

我试图编写一个安全遍历序列并尝试忽略MoveNext异常的方法。但是我不确定MoveNext当它抛出异常时是否提前位置,所以这也可能是无限循环。这也是个坏主意,因为我们将依赖于实现细节。

但这真是太有趣了!

public static IEnumerable<T> SafeWalk<T> (this IEnumerable<T> source)
{
    var enumerator = source.GetEnumerator();
    bool? hasCurrent = null;

    do {
        try {
            hasCurrent = enumerator.MoveNext();
        } catch {
            hasCurrent = null; // we're not sure
        }

        if (hasCurrent ?? false) // if not sure, do not return value
            yield return enumerator.Current;

    } while (hasCurrent ?? true); // if not sure, continue walking
}

foreach (var file in Directory.EnumerateFiles("c:\\", "*", SearchOption.AllDirectories)
                              .SafeWalk())
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这仅在框架实现此迭代器的以下条件为真时才有效(参见FileSystemEnumerableIterator<TSource>Reflector 以供参考):

  • MoveNext 失败时推进其位置;
  • MoveNext最后一个元素失败时,后续调用将返回false而不是抛出异常;
  • 这种行为对于不同版本的 .NET Framework 是一致的;
  • 我没有犯任何逻辑或语法错误。

即使它有效,也请不要在生产中使用它!
但我真的想知道它是否确实如此。

  • 不幸的是,MoveNext() 在抛出异常时并没有提升它的位置。 (4认同)
  • 像 DirectoryEnumerationPolicy.SkipUnauthorizedPaths 这样的东西作为 Directory.Enumerate{Directories,Files,FileSystemEntries} 的附加参数会很好。 (3认同)