2009-07-13 12 views
13

Voglio poter ottenere la dimensione di una delle directory locali usando C#. Sto cercando di evitare i seguenti (pseudo come il codice), anche se nel peggiore dei casi dovrò accontentare di questo:Come posso ottenere una dimensione di directory (file nella directory) in C#?

int GetSize(Directory) 
    { 
     int Size = 0; 

     foreach (File in Directory) 
     { 
      FileInfo fInfo of File; 
      Size += fInfo.Size; 
     } 

     foreach (SubDirectory in Directory) 
     { 
      Size += GetSize(SubDirectory); 
     } 
     return Size; 
    } 

In sostanza, v'è una passeggiata() disponibile da qualche parte in modo che io possa camminare attraverso l'albero delle directory? Quale salverebbe la ricorsione di passare attraverso ogni sottodirectory.

risposta

18

Se si utilizza Directory.GetFiles è possibile eseguire una ricerca ricorsiva (utilizzando SearchOption.AllDirectories), ma questo è comunque un po 'instabile (soprattutto se non si ha accesso a una delle sottodirectory) e potrebbe comportare un singolo enorme array che ritorna (avvertendo klaxon ...).

Sarei felice con l'approccio di ricorsione se non potessi mostrare (tramite il profiling) un collo di bottiglia; e quindi probabilmente passare a (livello singolo) Directory.GetFiles, utilizzando un Queue<string> per emulare la ricorsione.

Si noti che .NET 4.0 introduce alcuni metodi di elenco di file/elenchi basati su enumeratore che salvano sui grandi array.

+0

Grazie per questo :) – ThePower

0

Ho cercato qualche tempo fa per una funzione come quella che chiedi e da quello che ho trovato su Internet e nei forum MSDN, non esiste tale funzione.

Il modo ricorsivo è l'unico che ho trovato per ottenere la dimensione di una cartella considerando tutti i file e le sottocartelle che contiene.

4

Si potrebbe nascondere il vostro ricorsione dietro un metodo di estensione (per evitare i problemi di Marc ha evidenziato con il GetFiles() metodo):

public static IEnumerable<FileInfo> Walk(this DirectoryInfo directory) 
{ 
    foreach(FileInfo file in directory.GetFiles()) 
    { 
     yield return file; 
    } 

    foreach(DirectoryInfo subDirectory in directory.GetDirectories() 
    { 
     foreach(FileInfo file in subDirectory.Walk()) 
     { 
      yield return file; 
     } 
    } 
} 

(Probabilmente si desidera aggiungere un po 'di movimentazione a questo per cartelle protette eccezione ecc)

Poi:

int totalSize = 0; 

foreach(FileInfo file in directory.Walk()) 
{ 
    totalSize += file.Length; 
} 

In sostanza lo stesso codice, ma forse un po 'più ordinato ...

6

Qui il mio approccio NET 4.0

public static long GetFileSizeSumFromDirectory(string searchDirectory) 
{ 
var files = Directory.EnumerateFiles(searchDirectory); 

// get the sizeof all files in the current directory 
var currentSize = (from file in files let fileInfo = new FileInfo(file) select fileInfo.Length).Sum(); 

var directories = Directory.EnumerateDirectories(searchDirectory); 

// get the size of all files in all subdirectories 
var subDirSize = (from directory in directories select GetFileSizeSumFromDirectory(directory)).Sum(); 

return currentSize + subDirSize; 
} 

O ancora più bello:

// get IEnumerable from all files in the current dir and all sub dirs 
var files = Directory.EnumerateFiles(searchDirectory,"*",SearchOption.AllDirectories); 

// get the size of all files 
long sum = (from file in files let fileInfo = new FileInfo(file) select fileInfo .Length).Sum(); 

Come Gabriel ha sottolineato questa avrà esito negativo se si dispone di una directory ristretto sotto la searchDirectory!

+1

Piccolo errore di battitura sull'ultima riga di codice, dovrebbero essere: // ottenere la dimensione di tutti i file lungo sum = (da file in file lasciate fileinfo = new FileInfo (file) seleziona fileInfo.Length) .Sum(); –

+1

E se si ha una directory limitata sotto la directory search, fallirà! Per vedere quella risoluzione in una versione futura del framework: https://connect.microsoft.com/VisualStudio/feedback/details/512171/directory-enumeratedirectory-etc-unusable-due-to-frequent-unauthorizedaccessexceptions-even-runas- amministratore –

+0

Grazie per i tuoi commenti Gabriel. Ho corretto l'errore di battitura e incluso il possibile avviso di errore. – flayn

3

In primo luogo, perdona il mio povero inglese; o) ho avuto un problema che mi ha portato a questa pagina: enumerare i file di una directory e le sue sottodirectory senza bloccare su un UnauthorizedAccessException, e, come il nuovo metodo di .Net 4 DirectoryInfo .Enumerare ..., ottenere il primo risultato prima della fine dell'intera query.

Con l'aiuto di vari esempi trovati qua e là sul web, ho finalmente scrivere questo metodo:

public static IEnumerable<FileInfo> EnumerateFiles_Recursive(this DirectoryInfo directory, string searchPattern, SearchOption searchOption, Func<DirectoryInfo, Exception, bool> handleExceptionAccess) 
{ 
    Queue<DirectoryInfo> subDirectories = new Queue<DirectoryInfo>(); 
    IEnumerable<FileSystemInfo> entries = null; 

    // Try to get an enumerator on fileSystemInfos of directory 
    try 
    { 
     entries = directory.EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); 
    } 
    catch (Exception e) 
    { 
     // If there's a callback delegate and this delegate return true, we don't throw the exception 
     if (handleExceptionAccess == null || !handleExceptionAccess(directory, e)) 
      throw; 
     // If the exception wasn't throw, we make entries reference an empty collection 
     entries = EmptyFileSystemInfos; 
    } 

    // Yield return file entries of the directory and enqueue the subdirectories 
    foreach (FileSystemInfo entrie in entries) 
    { 
     if (entrie is FileInfo) 
      yield return (FileInfo)entrie; 
     else if (entrie is DirectoryInfo) 
      subDirectories.Enqueue((DirectoryInfo)entrie); 
    } 

    // If recursive search, we make recursive call on the method to yield return entries of the subdirectories. 
    if (searchOption == SearchOption.AllDirectories) 
    { 
     DirectoryInfo subDir = null; 
     while (subDirectories.Count > 0) 
     { 
      subDir = subDirectories.Dequeue(); 
      foreach (FileInfo file in subDir.EnumerateFiles_Recursive(searchPattern, searchOption, handleExceptionAccess)) 
      { 
       yield return file; 
      } 
     } 
    } 
    else 
     subDirectories.Clear(); 
} 

Io uso una coda e un metodo ricorsivo per mantenere l'ordine tradizionale (contenuto della directory e quindi il contenuto della prima sottodirectory e le sue sottodirectory e poi il contenuto del secondo ...). Il parametro "handleExceptionAccess" è solo una chiamata di funzione quando viene generata un'eccezione con una directory; la funzione deve restituire true per indicare che l'eccezione deve essere ignorata.

Con questo methode, è possibile scrivere:

DirectoryInfo dir = new DirectoryInfo("c:\\temp"); 
long size = dir.EnumerateFiles_Recursive("*", SearchOption.AllDirectories, (d, ex) => true).Sum(f => f.Length); 

Ed eccoci qui: tutti un'eccezione durante il tentativo di enumerare una directory sarà ignorare!

Spero che questo aiuto

Lionel

PS: per un motivo che non so spiegare, il mio metodo è più veloce rispetto al quadro 4 one ...

PPS: si può ottenere la mia provare le soluzioni con la fonte per questi metodi: qui TestDirEnumerate. Scrivo EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (usa una coda per evitare la ricorsione) e EnumerateFiles_NonRecursive_TraditionalOrder (usa una pila di code per evitare la ricorsione e mantenere l'ordine tradizionale). Mantenere quei 3 metodi non ha alcun interesse, li scrivo solo per testare il migliore. Penso di mantenere solo l'ultimo. Ho anche scritto l'equivalente per EnumerateFileSystemInfos e EnumerateDirectories.

+0

Ho fatto un misstake in questa versione perché cerco le sudirectories con searchPattern. Quindi se una sottodirectory non corrisponde a searchPattern, non la esploro ... [br] Ho scritto una nuova versione per correggere questo: ma, devo fare due richieste sulla directory: una con searchPattern, e un'altra senza searchPattern ==> le prestazioni non sono buone come prima. –

+0

Quindi aggiungo una versione senza searchpattern che sono più veloci. Guarda la mia soluzione di test qui: [link] (http://lionel.komsa.free.fr/temp/TestDirEnumerate2.zip) –

+0

Forse mi manca qualcosa, ma con questo metodo continuerai ad avere schede incomplete, a destra ? Se sono autorizzato a vedere le cartelle A e C ma non B, e 'Enumerate' esamina A, quindi B, poi C, si fermerà con B e non riuscirà a contare C. –

30

Un modo molto succinto per ottenere una dimensione di cartella in .net 4.0 è inferiore. Soffre ancora della limitazione di dover attraversare tutti i file in modo ricorsivo, ma non carica una serie potenzialmente enorme di nomi di file e sono solo due righe di codice.

private static long GetDirectorySize(string folderPath) 
    { 
     DirectoryInfo di = new DirectoryInfo(folderPath); 
     return di.EnumerateFiles("*.*", SearchOption.AllDirectories).Sum(fi => fi.Length); 
    } 
+0

+1 per LINQ'ness! –

+4

Ti consiglio di usare * invece di *. * perché i nomi dei file non richiedono a. – Joe

+4

Seguo il tuo ragionamento, ma penso che non sia necessario perché il sistema operativo Windows corrisponderà a *. * pattern rispetto ai nomi di file/cartelle, indipendentemente dal fatto che contengano un '.' o no. Penso che non faccia alcuna differenza, ma sarebbe interessato a sapere se ho torto. Naturalmente, se fosse Mono in esecuzione su un sistema operativo diverso, potrebbe essere diverso. – Kev

-2

Si dovrebbe rendere più facile su te stesso. Creare un metodo e passare la posizione della directory.

private static long GetDirectorySize(string location) { 
     return new DirectoryInfo(location).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length); 
    } 

-G

Problemi correlati