33

Hay un buen método nuevo en .NET 4.0 para obtener archivos en un directorio de forma continua a través de la enumeración.Directory.EnumerateFiles => UnauthorizedAccessException

El problema aquí es que si uno desea enumerar todos los archivos, es posible que uno no sepa con antelación qué archivos o carpetas tienen acceso protegido y puede lanzar una Access Access no autorizada.

reproducir, uno puede simplemente ejecutar este fragmento:

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

Antes de este método .NET existía era posible lograr más o menos el mismo efecto mediante la implementación de un iterador recursiva en la cadena de matriz regresar métodos. Pero no es tan vago como el nuevo método .NET.

¿Qué hacer? ¿Puede suprimirse la excepción de acceso no autorizado o es un hecho real al usar este método?

Me parece que el método debe tener una sobrecarga aceptando una acción para hacer frente a cualquier excepción.

+0

Sí, su método de vaciado() debe ser resistente a los problemas con los archivos que está intentando tirar. Dale una sobrecarga. –

+1

Ese no es mi problema Hans. El problema es que el avance del iterador de archivos (EnumerateFiles) provoca una excepción de acceso no autorizada y eso a su vez detiene la enumeración adicional, lo que no es deseable cuando se quiere un conjunto de resultados exhaustivo. –

+0

@Hans - El método 'Dump()' no es el problema aquí, simplemente recorre la enumeración de cadenas. El problema es el método 'Directory.EnumerateFiles'. Y no creo que haya una manera de manejar el problema. Usted tiene que recurrir a 'SearchOption.TopDirectoryOnly' y manejar la recursión usted mismo, creo. – Mormegil

Respuesta

5

Este problema con la respuesta anterior es que no se ocupa de la excepción en los subdirectorios. Esto sería una mejor manera de manejar las excepciones para que pueda obtener todos los archivos de todos los subdirectorios, excepto los que tienen inició una excepción de acceso:

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

Entiendo que es MoveNext que arroja la excepción.

Intenté escribir un método que conduzca con seguridad una secuencia e intente ignorar las excepciones MoveNext. Sin embargo, no estoy seguro de si MoveNext avanza la posición cuando arroja una excepción, por lo que podría ser un ciclo infinito. También es una mala idea porque dependeríamos de los detalles de implementación.

¡Pero es tan tan divertido!

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()) 
{ 
    // ... 
} 

Esto sólo funcionará si son ciertas acerca de la implementación del marco de este iterador las siguientes condiciones (ver FileSystemEnumerableIterator<TSource> en el reflector de referencia):

  • MoveNext avanza en su posición cuando se produce un error;
  • Cuando MoveNext falla en el último elemento, las llamadas subsiguientes devolverán false en lugar de lanzar una excepción;
  • Este comportamiento es coherente para las diferentes versiones de .NET Framework;
  • No he cometido ningún error de lógica o de sintaxis.

Incluso si funciona, nunca lo use en producción.
Pero realmente me pregunto si es así.

+1

No está mal, pero no funcionaba para mi situación: necesito que continúe y seguir adelante cuando se encuentra con una excepción: la única diferencia entre una caminata segura y una caminata normal es que la caminata segura simplemente detiene la enumeración mientras el método normal se detiene con una excepción. Necesito que continúe e ignore cualquier excepción en el sentido de que debe enumerar todos los directorios que puede y omitir aquellos a los que no tiene acceso. Eso, lamentablemente, parece que requiere una nueva implementación de la implementación de BCL. –

+0

... No me importaría utilizarlo en producción si funcionó ;-) ... pero incluso entonces necesitaría algunas modificaciones: por ejemplo, no desea capturar * todas * excepciones, debería ser solo UnaAnhorizedAccessException o al menos debe ser filtrable a través de una lambda. –

+3

Desafortunadamente, MoveNext() no avanza en su posición cuando arroja una excepción. – Dan

24

no pude conseguir el anterior para el trabajo, pero aquí está mi aplicación, que he probado en c: \ users en un cuadro de "Win7", porque si tiene todos estos directorios "desagradables":

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

Clase:

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

Gracias strudso. Esto parece funcionar. –

+0

Buena respuesta. ¡Gracias! –

+1

También encontré un problema con esto. La solución que surgió se puede encontrar en http://stackoverflow.com/questions/13130052/directoryinfo-enumeratefiles-causes-unauthorizedaccessexception-and-other. Se comporta como un verdadero enumerable en el sentido de que solo funciona si solicita el siguiente artículo. –

0

Basado en la respuesta de strudso, pero como los métodos de extensión, tanto para FileInfo y DirectoryInfo.

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

En realidad, no estoy seguro de si esta es la mejor opción. ¿Qué pasaría si fuera Enumerate * se pusiera un poco más flojo y lanzara una excepción durante la enumeración? – Fowl

Cuestiones relacionadas