2012-10-29 11 views
9

Ho recentemente avuto bisogno di Enumerare un intero file system alla ricerca di tipi specifici di file a scopo di controllo. Questo mi ha fatto correre in diverse eccezioni a causa di avere permessi limitati sul file system per essere scansionati. Tra questi, i più diffusi sono stati UnauthorizedAccessException e con mio grande dispiacere, PathTooLongException.DirectoryInfo.EnumerateFiles (...) causa UnauthorizedAccessException (e altre eccezioni)

Questi non sarebbero normalmente un problema tranne che invalidano l'IEnumerable, impedendomi di essere in grado di completare la scansione.

risposta

14

Per risolvere questo problema, ho creato un enumeratore file system sostitutivo. Anche se potrebbe non essere perfetto, funziona abbastanza velocemente e intrappola le due eccezioni in cui mi sono imbattuto. Troverà qualsiasi directory o file che corrispondono al modello di ricerca passato ad esso.

// This code is public domain 
using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using log4net; 

public class FileSystemEnumerable : IEnumerable<FileSystemInfo> 
{ 
    private ILog _logger = LogManager.GetLogger(typeof(FileSystemEnumerable)); 

    private readonly DirectoryInfo _root; 
    private readonly IList<string> _patterns; 
    private readonly SearchOption _option; 

    public FileSystemEnumerable(DirectoryInfo root, string pattern, SearchOption option) 
    { 
     _root = root; 
     _patterns = new List<string> { pattern }; 
     _option = option; 
    } 

    public FileSystemEnumerable(DirectoryInfo root, IList<string> patterns, SearchOption option) 
    { 
     _root = root; 
     _patterns = patterns; 
     _option = option; 
    } 

    public IEnumerator<FileSystemInfo> GetEnumerator() 
    { 
     if (_root == null || !_root.Exists) yield break; 

     IEnumerable<FileSystemInfo> matches = new List<FileSystemInfo>(); 
     try 
     { 
      _logger.DebugFormat("Attempting to enumerate '{0}'", _root.FullName); 
      foreach (var pattern in _patterns) 
      { 
       _logger.DebugFormat("Using pattern '{0}'", pattern); 
       matches = matches.Concat(_root.EnumerateDirectories(pattern, SearchOption.TopDirectoryOnly)) 
           .Concat(_root.EnumerateFiles(pattern, SearchOption.TopDirectoryOnly)); 
      } 
     } 
     catch (UnauthorizedAccessException) 
     { 
      _logger.WarnFormat("Unable to access '{0}'. Skipping...", _root.FullName); 
      yield break; 
     } 
     catch (PathTooLongException ptle) 
     { 
      _logger.Warn(string.Format(@"Could not process path '{0}\{1}'.", _root.Parent.FullName, _root.Name), ptle); 
      yield break; 
     } catch (System.IO.IOException e) 
     { 
      // "The symbolic link cannot be followed because its type is disabled." 
      // "The specified network name is no longer available." 
      _logger.Warn(string.Format(@"Could not process path (check SymlinkEvaluation rules)'{0}\{1}'.", _root.Parent.FullName, _root.Name), e); 
      yield break; 
     } 


     _logger.DebugFormat("Returning all objects that match the pattern(s) '{0}'", string.Join(",", _patterns)); 
     foreach (var file in matches) 
     { 
      yield return file; 
     } 

     if (_option == SearchOption.AllDirectories) 
     { 
      _logger.DebugFormat("Enumerating all child directories."); 
      foreach (var dir in _root.EnumerateDirectories("*", SearchOption.TopDirectoryOnly)) 
      { 
       _logger.DebugFormat("Enumerating '{0}'", dir.FullName); 
       var fileSystemInfos = new FileSystemEnumerable(dir, _patterns, _option); 
       foreach (var match in fileSystemInfos) 
       { 
        yield return match; 
       } 
      } 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

L'utilizzo è abbastanza semplice.

//This code is public domain 
var root = new DirectoryInfo(@"c:\wherever"); 
var searchPattern = @"*.txt"; 
var searchOption = SearchOption.AllDirectories; 
var enumerable = new FileSystemEnumerable(root, searchPattern, searchOption); 

Le persone sono libere di usarlo se lo trovano utile.

+0

"Le persone sono libere di usarlo se lo trovano utile." : non è proprio vero. il tuo esercizio in CC BY SA 3, (commons creative) che lo rende delicato da usare come dato di fatto. È possibile indicare in modo esplicito la licenza "dominio pubblico" (copyleft) o zlib (la più debole licenza di copyright) nel frammento di codice in un commento. Grazie. –

+1

Asserisco che il codice nella risposta sopra al 14 luglio 2015 è di dominio pubblico con tutti i diritti e privilegi che concede. –

+1

Ho trovato che avevo bisogno di prendere 'System.IO.IOException' per le situazioni in cui sto camminando su un'unità di rete che ha una directory simlink da remoto a locale con tali regole di espansione simlink [disabilitate sulla macchina corrente] (http: // blogs.msdn.com/b/junfeng/archive/2012/05/07/the-symbolic-link-cannot-be-followed-because-its-type-is-disabled.aspx). Ho modificato la tua risposta di conseguenza. Ricorre anche all'infinito se incontra un collegamento simbolico che punta a una cartella degli antenati; nel mio caso ho semplicemente ignorato dirs con attributi di 'FileAttributes.ReparsePoint', ma probabilmente non è abbastanza elegante per una risposta generale – RJFalconer

Problemi correlati