33

有在.NET 4.0中一个不错的新方法,通过列举的方式流获取目录中的文件。Directory.EnumerateFiles => UnauthorizedAccessException

这里的问题是,如果一个人希望枚举所有文件中的一个可能不会提前知道哪些文件或文件夹的访问保护,可以抛出UnauthorizedAccessException。

要重现,一个可以运行这个片段:

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

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

所以,我们要做什么呢?在使用这种方法时,UnauthorizedAccessException可以被抑制还是生活中的事实?

在我看来,该方法应该有一个接受过载的作用,以应付任何异常。

+0

是的,你的dump()方法应该是弹性与它正试图在文件中的问题转储。给它一个超负荷。 – 2011-02-23 22:33:03

+1

这不是我的问题汉斯。问题在于对文件迭代器(EnumerateFiles)的foreach引发了一个UnauthorizedAccessException异常,并停止了进一步的枚举,当需要详尽的结果集时这是不可取的。 – 2011-02-23 22:36:51

+0

@Hans - 'Dump()'方法在这里不是问题,它只是遍历字符串枚举。问题是'Directory.EnumerateFiles'方法本身。我不认为有办法解决这个问题。我相信你必须求助于'SearchOption.TopDirectoryOnly'并且自己处理递归。 – Mormegil 2011-02-23 22:37:11

回答

5

这个问题与上述答案是没有照顾子目录中的异常。这将是一个更好的方式来处理这些异常,所以你除了那些所有子目录中得到的所有文件抛出访问异常:

/// <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; 
    } 
1

我知道这是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()) 
{ 
    // ... 
} 

这只会如果满足下列条件,有关框架的实现这个迭代器的真实工作(见FileSystemEnumerableIterator<TSource>在反射器供参考):

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

即使它工作,请不要在生产中使用它!
但我真的想知道它是否。

+1

不错,但它对我的情景没有任何作用:我需要它继续并在遇到异常时继续前进:安全步行和正常步行之间的唯一区别在于安全步行只是停止枚举,而正常的方法停止并出现异常。我需要它继续并忽略任何异常,因为它应该枚举它可以访问的所有目录,并跳过它无法访问的目录。不幸的是,似乎需要新的BCL实施实施。 – 2011-03-17 00:01:02

+0

......如果它在工作中使用,我不会在使用它的时候出现问题;-)但即使如此,它也需要进行一些修改:例如,您不想捕获所有异常,它应该只是UnauthorizedAccessException或至少它应该可以通过lambda进行过滤。 – 2011-03-17 00:02:41

+3

不幸的是,MoveNext()在抛出异常时并没有提前它的位置。 – Dan 2015-02-26 16:23:01

24

我不能得到上述工作,但这里是我的实现,我已经测试了C:\用户在“Win7的”框,因为如果有这方面的“讨厌”迪尔斯:

SafeWalk.EnumerateFiles(@"C:\users", "*.jpg", SearchOption.AllDirectories).Take(10) 

类:

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>(); 
     } 
    } 
} 
+0

谢谢strudso。这似乎工作。 – 2011-07-08 18:15:17

+0

很好的答案。谢谢! – 2011-10-31 16:45:10

+1

我也遇到了这个问题。我想出的解决方案可以在http://stackoverflow.com/questions/13130052/directoryinfo-enumeratefiles-causes-unauthorizedaccessexception-and-other找到。它的行为就像一个真正的枚举类型,因为它只有在你向它请求下一个项目时才起作用。 – 2012-11-20 19:18:05

0

基于strudso的答案,但作为两个FileInfoDirectoryInfo扩展方法。

public static IEnumerable<FileInfo> EnumerateFilesSafe(this DirectoryInfo dir, string filter = "*.*", SearchOption opt = SearchOption.TopDirectoryOnly) 
{ 
    var retval = Enumerable.Empty<FileInfo>(); 

    try { retval = dir.EnumerateFiles(filter); } 
    catch { Debug.WriteLine("{0} Inaccessable.", dir.FullName); } 

    if (opt == SearchOption.AllDirectories) 
     retval = retval.Concat(dir.EnumerateDirectoriesSafe(opt: opt).SelectMany(x => x.EnumerateFilesSafe(filter, opt))); 

    return retval; 
} 

public static IEnumerable<DirectoryInfo> EnumerateDirectoriesSafe(this DirectoryInfo dir, string filter = "*.*", SearchOption opt = SearchOption.TopDirectoryOnly) 
{ 
    var retval = Enumerable.Empty<DirectoryInfo>(); 

    try { retval = dir.EnumerateDirectories(filter); } 
    catch { Debug.WriteLine("{0} Inaccessable.", dir.FullName); } 

    if (opt == SearchOption.AllDirectories) 
     retval = retval.Concat(retval.SelectMany(x => x.EnumerateDirectoriesSafe(filter, opt))); 

    return retval; 
} 
+0

其实,我不确定这是否是最佳选择。如果它是Enumerate,会发生什么?在枚举过程中会有更多的懒惰和抛出异常? – Fowl 2012-01-23 03:46:00

相关问题