33

.NET 4.0에는 열거 방식을 통해 스트리밍 방식으로 디렉터리에 파일을 가져 오는 멋진 새로운 방법이 있습니다.Directory.EnumerateFiles => UnauthorizedAccessException

여기에서 문제는 모든 파일을 열거하기를 원한다면 어떤 파일이나 폴더가 액세스 보호되어 있는지 미리 알지 못하고 UnauthorizedAccessException을 throw 할 수 있다는 것입니다.

한 막 단편을 실행할 수 있고, 재현이 .NET 방법은 대략 방법 복귀 문자열 배열 재귀 반복기를 구현하여 동일한 효과를 얻을 수 있었다 존재

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

전에. 그러나 새로운 .NET 방법만큼이나 게으른 것은 아닙니다.

그럼 어떻게해야합니까? 이 메소드를 사용하면 UnauthorizedAccessException을 억제 할 수 있습니까?

메서드에 예외를 처리하기위한 작업을 수락하는 오버로드가 있어야합니다.

+0

것은 예, 덤프() 메소드가하려고하는 파일 문제에 탄력적이어야한다 : 당신이있는 사람을 제외한 모든 하위 디렉터리에서 모든 파일을 가져 액세스 예외를 던졌다 그래서이 그 예외를 처리하는 더 좋은 방법이 될 것입니다 덤프. 과부하를주십시오. –

+1

그건 내 문제가 아니야. 한스. 문제는 파일 반복자 (EnumerateFiles)를 foreaching하면 UnauthorizedAccessException가 발생하고 차례로 더 많은 열거가 중지되므로 철저한 결과 집합을 원할 때 바람직하지 않습니다. –

+0

@Hans -'Dump()'메서드는 문제가 아니며 단지 문자열 열거를 처리합니다. 문제는'Directory.EnumerateFiles' 메소드 자체입니다. 그리고 나는 문제를 처리 할 방법이 있다고 생각하지 않는다. 'SearchOption.TopDirectoryOnly'에 의지하여 직접 재귀를 처리해야합니다. – Mormegil

답변

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

나는 예외를 throw하는 것이 MoveNext이라는 것을 알고 있습니다.

시퀀스를 안전 도보로 처리하고 MoveNext 예외를 무시하는 메서드를 작성하려고했습니다. 그러나 MoveNext 진도 위치가 예외를 throw 할 경우 확실하지 않아서 무한 루프가 될 수도 있습니다. 우리가 구현 세부 사항에 의존하기 때문에 나쁜 생각이기도합니다.

그러나 그것은 단지 재미 있습니다!

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이 마지막 요소에서 실패하면 후속 호출은 예외를 throw하는 대신 false을 반환합니다.
  • 이 동작은 .NET Framework의 여러 버전에서 일관됩니다.
  • 논리 또는 구문 오류를 만들지 않았습니다.

작동하는 경우에도 프로덕션 환경에서는 사용하지 마십시오.
하지만 실제로 그렇게되는지 궁금합니다.

+1

나쁘지는 않지만 내 시나리오에서는 작동하지 않습니다. 예외가 발생하면 계속 진행하고 계속해야합니다. 안전 산책과 정상 산책의 유일한 차이점은 안전 산책이 열거를 중지한다는 것입니다. 정상적인 메소드는 예외로 중지합니다. 나는 그것이 할 수있는 모든 디렉토리를 열거하고 액세스 할 수없는 디렉토리를 건너 뛰어야한다는 점에서 모든 예외를 무시하고 무시해야한다. 유감스럽게도 BCL 구현의 새로운 구현이 필요합니다. –

+0

... 나는 그것이 작동한다면 프로덕션에서 아무런 문제가 없다 ;-) ... 그렇지만 약간의 수정이 필요하다 : 예를 들어 * 모든 예외를 잡으려고하지 않는다면, 그냥 있어야한다. UnauthorizedAccessException 또는 적어도 람다를 통해 필터링 가능해야합니다. –

+3

불행히도 MoveNext()는 예외를 throw 할 때 위치를 전진하지 않습니다. – Dan

24

나는 위의 내 구현 여기에 작동하지만 가져올 수 없습니다, 전 C에 그것을 테스트했습니다는 "Win7에"상자에 사용자 \ 때문에 모든 "불쾌한"DIRS이있는 경우 :

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

등급 : FileInfoDirectoryInfo 모두

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

고마워요. 이것은 효과가있는 것처럼 보입니다. –

+0

좋은 답변입니다. 고맙습니다! –

+1

나는 또한 이것에 문제가있다. 내가 생각해 낸 해결책은 http://stackoverflow.com/questions/13130052/directoryinfo-enumeratefiles-causes-unauthorizedaccessexception-and-other에서 찾을 수 있습니다. 그것은 당신이 그것에서 다음 항목을 요구하는 경우에만 작동한다는 의미에서 진정한 열거 형으로 작동합니다. –

0
strudso의 답변에 따라

하지만 같은 확장 방법.

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

관련 문제