2009-09-08 38 views
22

Directory.GetFiles method falla en el primer encuentro con una carpeta a la que no tiene derechos de acceso.UnauthorizedAccessException no puede resolver el error Directory.GetFiles

El método arroja una excepción de acceso no autorizada (que se puede capturar) pero cuando se hace esto, el método ya ha fallado/terminado.

El código que estoy utilizando se enumeran a continuación:

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

Por lo que yo sé, no hay manera de comprobar de antemano si una determinada carpeta ha definido derechos de acceso.

En mi ejemplo, estoy buscando en un disco a través de una red y cuando me encuentro con una carpeta de acceso raíz, mi programa falla.

Respuesta

17

Para obtener el control en el nivel que desea, probablemente debería probar un directorio a la vez, en lugar de un árbol completo. El siguiente método rellena la IList<string> dado con todos los archivos que se encuentran en el árbol de directorios, excepto aquellos en los que el usuario no tiene acceso:

// 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

voy a probar esto y en contacto con usted. ¿Podría explicar lo que hace el operador '=>'? Gracias – Ric

+3

@Ric: '=>' es el operador lambda. Puede leer acerca de las expresiones lambda en C# aquí: http://msdn.microsoft.com/en-us/library/bb397687.aspx –

+0

¡Perfecto! Cheers Fredrik – Ric

3

En .NET 4 esto se convierte en mucho más fácil, ver http://msdn.microsoft.com/en-us/library/dd997370.aspx

+2

No estoy seguro de que esto sea exactamente cierto: Directory.EnumerateFiles sigue causando la misma excepción. –

+5

Pero puede atraparlo y manejarlo por elemento. –

4

. Directory.EnumerateFiles de .NET 4 funciona, pero debe tener cuidado al evaluar el enumerable y hacer esa parte dentro del bloque try-catch. El mayor problema es asegurarse de no detener el procesamiento en la primera excepción (que creo que la respuesta https://stackoverflow.com/a/1393219/89584 anterior tiene este problema, corrígeme si me equivoco).

las siguientes obras y le da una Enumerable por lo que no tiene que evaluar todo el árbol de archivos si usted está buscando para el primer partido, etc.

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); 
} 

mejoras a lo anterior bienvenida.

+0

Este ejemplo no funciona (var files = dirInfo.EnumerateFiles(). GetEnumerator();) arroja el error –

+0

@sjorsmiltenburg - reparado y actualizado. Terminé encontrándome con ese problema y encontré una solución, luego olvidé actualizar mi respuesta. – Malcolm

+0

Este código no funcionará según lo previsto. El 'EnumerateFiles' crea un objeto' FileSystemEnumerableIterator'. Cuando se llama al método 'MoveNext' mientras se itera el enumerador, el iterador se elimina en caso de CUALQUIER' IOException'. '.ElementAt' clona su iterador e itera sobre todos los elementos anteriores hasta que alcanza el índice solicitado. Esto significa que todos estos clones de iterador van a fallar en el mismo archivo inaccesible exacto. En otras palabras, el truco 'Enumerable.Range.Select {.ElementAt}' no está haciendo nada. – Paya

4

Esto es una mejora de la respuesta de Malcolm (http://stackoverflow.com/a/9831340/226181). Esto escanea todas las unidades lógicas para un patrón de coincidencia de archivos e ignora los directorios que no son accesibles.

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

Sé que este hilo es viejo, pero en caso de que alguien se tropieza con esto y necesita una respuesta, tengo una solución recursiva aquí:

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

devuelve un List<string> que contiene la ruta completa a todos archivos que están en directorios accesibles debajo del directorio raíz dado. Llámelo como esto:

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

Así que uno de los resultados podría ser así:

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 

espero que ayude a alguien!

+1

¡esto me salva el día! ¡gracias Señor! –

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

Esta es mi solución para este problema. Simple y a prueba de fallas.

+0

En la línea Directory.GetDirectories, tuve que reemplazar search_pattern por "*", lo cual tiene sentido considerando que generalmente quiere que el filtro esté en los archivos, no en los archivos y las carpetas. –

0

La versión más simple:

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>(); 
      } 
     })); 
} 
Cuestiones relacionadas