2009-09-08 4 views
22

Directory.GetFiles method сбой при первой встрече с папкой, к которой у нее нет прав доступа.UnauthorizedAccessException не может разрешить сбой Directory.GetFiles

Метод выдает исключение UnauthorizedAccessException (которое может быть уловлено), но к тому времени, когда это будет выполнено, метод уже не выполнен/завершен.

кода я использую, перечислен ниже:

try 
{ 
    // looks in stated directory and returns the path of all files found     
    getFiles = Directory.GetFiles(
     @directoryToSearch, 
     filetype, 
     SearchOption.AllDirectories);    
} 
catch (UnauthorizedAccessException) 
{ 
} 

Насколько мне известно, нет никакого способа, чтобы заранее проверить, является ли определенная папка определенных прав доступа.

В моем примере, я ищу на диске по сети, и когда я сталкиваюсь с корневой папкой только для доступа, моя программа выходит из строя.

ответ

17

Чтобы получить контроль над уровнем, который вы хотите, вы, вероятно, должны исследовать один каталог за раз, а не целое дерево. Следующий метод заполнит данный IList<string> со всеми найденными файлами в дереве каталогов, за исключением тех, где пользователь не имеет доступа:

// using System.Linq 
private static void AddFiles(string path, IList<string> files) 
{ 
    try 
    { 
     Directory.GetFiles(path) 
      .ToList() 
      .ForEach(s => files.Add(s)); 

     Directory.GetDirectories(path) 
      .ToList() 
      .ForEach(s => AddFiles(s, files)); 
    } 
    catch (UnauthorizedAccessException ex) 
    { 
     // ok, so we are not allowed to dig into that directory. Move on. 
    } 
} 
+0

Постараюсь это и вернусь к вам. Не могли бы вы объяснить, что делает оператор «=>»? Спасибо – Ric

+3

@Ric: '=>' это лямбда-оператор. Вы можете прочитать о лямбда-выражениях на C# здесь: http://msdn.microsoft.com/en-us/library/bb397687.aspx –

+0

Отлично! Cheers Fredrik – Ric

3

В .NET 4 это становится намного проще, см http://msdn.microsoft.com/en-us/library/dd997370.aspx

+2

Я не уверен, что это точно так - Directory.EnumerateFiles все равно вызывает выброс одного и того же исключения. –

+5

Но вы можете поймать его и обработать его по отдельности. –

4

.Net 4's Directory.EnumerateFiles работает, но вы должны быть осторожны, как вы оцениваете перечислимое количество и выполняете эту часть внутри блока try-catch. Самая большая проблема заключается в том, чтобы убедиться, что вы не прекращаете обработку в первом исключении (что, по моему мнению, ответ https://stackoverflow.com/a/1393219/89584 выше, эта проблема, пожалуйста, поправьте меня, если я ошибаюсь).

следующие работы и дает вам перечислимое, так что вы не должны оценить все дерево файла, если вы ищете для первого матча и т.д.

private IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse) 
{ 
    IEnumerable<String> emptyList = new string[0]; 

    if (File.Exists(path)) 
    return new string[] { path }; 

    if (!Directory.Exists(path)) 
    return emptyList; 

    var top_directory = new DirectoryInfo(path); 

    // Enumerate the files just in the top directory. 
    var files = top_directory.EnumerateFiles(file_pattern); 
    var filesLength = files.Count(); 
    var filesList = Enumerable 
      .Range(0, filesLength) 
      .Select(i => 
      { 
       string filename = null; 
       try 
       { 
       var file = files.ElementAt(i); 
       filename = file.FullName; 
       } 
       catch (UnauthorizedAccessException) 
       { 
       } 
       catch (InvalidOperationException) 
       { 
        // ran out of entries 
       } 
       return filename; 
      }) 
      .Where(i => null != i); 

     if (!recurse) 
      return filesList; 

     var dirs = top_directory.EnumerateDirectories("*"); 
     var dirsLength = dirs.Count(); 
     var dirsList = Enumerable 
      .Range(0, dirsLength) 
      .SelectMany(i => 
      { 
       string dirname = null; 
       try 
       { 
       var dir = dirs.ElementAt(i); 
       dirname = dir.FullName; 
       return FindAccessableFiles(dirname, file_pattern, required_extension, recurse); 
       } 
       catch (UnauthorizedAccessException) 
       { 
       } 
       catch (InvalidOperationException) 
       { 
       // ran out of entries 
       } 

       return emptyList; 
      }) 

    return Enumerable.Concat(filesList, dirsList); 
} 

улучшение вышеприведенном приема.

+0

Этот пример не работает (var files = dirInfo.EnumerateFiles(). GetEnumerate();) выдает ошибку –

+0

@sjorsmiltenburg - исправлено и обновлено. Я столкнулся с этой проблемой и нашел обходной путь, а затем забыл обновить свой ответ. – Malcolm

+0

Этот код не работает должным образом. 'EnumerateFiles' создает объект' FileSystemEnumerableIterator'. При вызове метода MoveNext при повторении перечислителя итератор удаляется в случае ЛЮБОГО 'IOException'. '.ElementAt' клонирует ваш итератор и выполняет итерации по всем предыдущим элементам, пока не достигнет запрошенного индекса. Это означает, что все эти итерационные клоны будут терпеть неудачу в том же недоступном файле. Другими словами, трюк 'Enumerable.Range.Select {.ElementAt}' ничего не делает. – Paya

4

Это - улучшение ответа Малкольма (http://stackoverflow.com/a/9831340/226181). Это проверяет все логические диски для шаблона соответствия файла и игнорирует недоступные каталоги.

static List<string> SearchFiles(string pattern) 
    { 
     var result = new List<string>(); 

     foreach (string drive in Directory.GetLogicalDrives()) 
     { 
      Console.WriteLine("searching " + drive); 
      var files = FindAccessableFiles(drive, pattern, true); 
      Console.WriteLine(files.Count().ToString() + " files found."); 
      result.AddRange(files); 
     } 

     return result; 
    } 

    private static IEnumerable<String> FindAccessableFiles(string path, string file_pattern, bool recurse) 
    { 
     Console.WriteLine(path); 
     var list = new List<string>(); 
     var required_extension = "mp4"; 

     if (File.Exists(path)) 
     { 
      yield return path; 
      yield break; 
     } 

     if (!Directory.Exists(path)) 
     { 
      yield break; 
     } 

     if (null == file_pattern) 
      file_pattern = "*." + required_extension; 

     var top_directory = new DirectoryInfo(path); 

     // Enumerate the files just in the top directory. 
     IEnumerator<FileInfo> files; 
     try 
     { 
      files = top_directory.EnumerateFiles(file_pattern).GetEnumerator(); 
     } 
     catch (Exception ex) 
     { 
      files = null; 
     } 

     while (true) 
     { 
      FileInfo file = null; 
      try 
      { 
       if (files != null && files.MoveNext()) 
        file = files.Current; 
       else 
        break; 
      } 
      catch (UnauthorizedAccessException) 
      { 
       continue; 
      } 
      catch (PathTooLongException) 
      { 
       continue; 
      } 

      yield return file.FullName; 
     } 

     if (!recurse) 
      yield break; 

     IEnumerator<DirectoryInfo> dirs; 
     try 
     { 
      dirs = top_directory.EnumerateDirectories("*").GetEnumerator(); 
     } 
     catch (Exception ex) 
     { 
      dirs = null; 
     } 


     while (true) 
     { 
      DirectoryInfo dir = null; 
      try 
      { 
       if (dirs != null && dirs.MoveNext()) 
        dir = dirs.Current; 
       else 
        break; 
      } 
      catch (UnauthorizedAccessException) 
      { 
       continue; 
      } 
      catch (PathTooLongException) 
      { 
       continue; 
      } 

      foreach (var subpath in FindAccessableFiles(dir.FullName, file_pattern, recurse)) 
       yield return subpath; 
     } 
    } 
4

Я знаю, эта нить старый, но в случае, если кто-то натыкается на это и требует ответа, я получил рекурсивное решение здесь:

public static List<string> GetAllAccessibleFiles(string rootPath, List<string> alreadyFound = null) 
    { 
     if (alreadyFound == null) 
      alreadyFound = new List<string>(); 
     DirectoryInfo di = new DirectoryInfo(rootPath); 
     var dirs = di.EnumerateDirectories(); 
     foreach (DirectoryInfo dir in dirs) 
     { 
      if (!((dir.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden)) 
      { 
       alreadyFound = GetAllAccessibleFiles(dir.FullName, alreadyFound); 
      } 
     } 

     var files = Directory.GetFiles(rootPath); 
     foreach (string s in files) 
     { 
      alreadyFound.Add(s);     
     } 

     return alreadyFound; 
    } 

Она возвращает List<string>, содержащий полный путь ко всем файлы, находящиеся в доступных каталогах ниже указанного корневого каталога. Назовите это так:

var files = GetAllAccessibleFiles(@"C:\myDirectory"); 

Так один результат может быть такой:

C:\myDirectory\a\a.txt 
C:\myDirectory\a\b.mp3 
C:\myDirectory\b\a\a\foo.txt 
C:\myDirectory\b\b\b\hello.exe 
C:\myDirectory\b\c\bar.jpg 
C:\myDirectory\and\so\on.bar 
C:\myDirectory\a_file_in_root.bmp 

Надеется, что это помогает кто-то!

+1

это экономит мой день! Спасибо, сэр! –

1
public string[] GetFilesFrom(string dir, string search_pattern, bool recursive) 
{ 
    List<string> files = new List<string>(); 

    string[] temp_files = new string[0]; 

    try { temp_files = Directory.GetFiles(dir, search_pattern, SearchOption.TopDirectoryOnly); } 
    catch { } 

    files.AddRange(temp_files); 

    if (recursive) 
    { 
     string[] temp_dirs = new string[0]; 

     try { temp_dirs = Directory.GetDirectories(dir, search_pattern, SearchOption.TopDirectoryOnly); } 
     catch { } 

     for (int i = 0; i < temp_dirs.Length; i++) 
      files.AddRange(GetFilesFrom(temp_dirs[i], search_pattern, recursive)); 
    } 

    return files.ToArray(); 
} 

Это мое решение этой проблемы. Простой и отказоустойчивый.

+0

В строке Directory.GetDirectories мне пришлось заменить search_pattern на «*», что имеет смысл, поскольку вы обычно хотите, чтобы фильтр находился в файлах, а не как файлы, так и папки. –

0

Самый простой вариант:

IEnumerable<String> GetAllFiles(string path, string searchPattern) 
{ 
    return System.IO.Directory.EnumerateFiles(path, searchPattern).Union(
     System.IO.Directory.EnumerateDirectories(path).SelectMany(d => 
     { 
      try 
      { 
       return GetAllFiles(d, searchPattern); 
      } 
      catch (UnauthorizedAccessException e) 
      { 
       return Enumerable.Empty<String>(); 
      } 
     })); 
}