2009-07-13 18 views
13

Chcę móc uzyskać rozmiar jednego z lokalnych katalogów przy użyciu C#. Staram się unikać następujący (pseudo takiego kodu), chociaż w najgorszym wypadku będę musiał zadowolić się następująco:Jak uzyskać rozmiar katalogu (pliki w katalogu) w języku 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; 
    } 

Zasadniczo, jest tam na spacer() dostępne gdzieś tak, że mogę chodzić przez drzewo katalogów? Który zapisałby rekursję przechodzenia przez każdy podkatalog.

Odpowiedz

18

Jeśli używasz Directory.GetFiles można zrobić rekurencyjną wyszukiwanego (używając SearchOption.AllDirectories), ale jest to trochę łuszcząca się i tak (zwłaszcza jeśli nie masz dostępu do jednego z podkatalogów) - i może pociągać za sobą ogromne single tablica wraca (ostrzeżenie klaxon ...).

Byłbym zadowolony z podejścia rekursji, chyba że mógłbym pokazać (poprzez profilowanie) wąskie gardło; a następnie prawdopodobnie przestawiłbym się na (jednopoziomowy) Directory.GetFiles, używając Queue<string> do emulowania rekursji.

Należy zauważyć, że .NET 4.0 wprowadza niektóre metody listowania plików/katalogów oparte na modułach wyliczających, które można zapisać na dużych tablicach.

+0

Dzięki za to :) – ThePower

0

Już od jakiegoś czasu szukałem funkcji podobnej do tej, o którą prosiłem i z tego, co znalazłem w Internecie i na forach MSDN, nie ma takiej funkcji.

Rekurencyjna metoda to jedyna opcja, w której znalazłem rozmiar folderu z uwzględnieniem wszystkich zawartych w nim plików i podfolderów.

4

Można ukryć swój rekursji za metodę rozszerzenia (aby uniknąć problemów Marc uwidocznił z GetFiles() metoda):

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

(Prawdopodobnie chcesz dodać wyjątek obsługi to dla folderów zabezpieczonych itd.)

Następnie:

int totalSize = 0; 

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

zasadzie ten sam kod, ale może trochę neater ...

6

Oto mój .NET 4.0 podejście

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

Albo nawet ładniejszy:

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

Gabriel wskazał będzie to powiedzie się, jeśli masz ograniczony katalog pod searchDirectory!

+1

Mały typo na ostatniej linii kodu, powinno być: // uzyskać rozmiar wszystkich plików długo suma = (z pliku w plikach niech Fileinfo = new FileInfo (plik) wybierz plikInfo.Length) .Sum(); –

+1

A jeśli masz zastrzeżony katalog w katalogu wyszukiwania, to się nie uda! Aby zobaczyć to rozwiązanie w przyszłej wersji architektury: https://connect.microsoft.com/VisualStudio/feedback/details/512171/directory-enumeratedirectory-etc-unusable-due-to-frequent-unauthorizedaccessexceptions-even-runas- administrator –

+0

Dzięki za komentarze Gabriel. Naprawiłem literówkę i zawarłem możliwe ostrzeżenie o błędzie. – flayn

3

Po pierwsze, wybacz mój biedny angielski; o) Miałem problem, który zabrał mnie na tę stronę: wyliczyć pliki katalogu i jego podkatalogów bez blokowania na wyjątku UnauthorizedAccessException i, podobnie jak nowa metoda .Net 4 DirectoryInfo .Zanumeruj ..., uzyskaj pierwszy wynik przed zakończeniem całego zapytania.

Z pomocą różnych przykładów znalezionych tu i tam w internecie, w końcu napisać tę metodę:

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

używać kolejki i metody rekurencyjnej zachować tradycyjny porządek (zawartość katalogu, a następnie zawartość pierwszego podkatalogu i jego własnych podkatalogów, a następnie zawartość drugiego ...). Parametr "handleExceptionAccess" jest po prostu wywołaniem funkcji, gdy zostanie zgłoszony wyjątek z katalogiem; funkcja musi zwracać wartość true, aby wskazać, że wyjątek musi zostać zignorowany.

Z tego Methode, można napisać:

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

I tu jesteśmy: wszystkie wyjątek, gdy próbuje wyliczyć katalog będzie ignorować!

Nadzieja ta pomoc

Lionel

PS: z jakiegoś powodu nie mogę wyjaśnić, moja metoda jest bardziej szybkie niż jeden Framework 4 ...

PPS: można dostać mój testowe rozwiązania ze źródłem dla tych metod: tutaj TestDirEnumerate. Piszę EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (użyj kolejki, aby uniknąć rekursji) i EnumerateFiles_NonRecursive_TraditionalOrder (użyj stosu kolejki, aby uniknąć rekursji i zachować tradycyjną kolejność). Trzymaj te 3 metody bez zainteresowania, piszę je tylko dla sprawdzenia najlepszego. Myślę, że zatrzymam tylko ostatnią. Napisałem również odpowiednik dla EnumerateFileSystemInfos i EnumerateDirectories.

+0

Zrobiłem błąd w tej wersji, ponieważ przeszukuję katalogi z wyszukiwarką. Więc jeśli podkatalogu nie pasuje do searchPattern, nie badam go ... [br] Napisałem nową wersję, aby to poprawić: ale, muszę zrobić dwie prośby w katalogu: jeden z searchPattern, a drugi bez searchPattern ==> wydajność nie jest tak dobra jak poprzednio. –

+0

Dodaję wersję bez wyszukiwania, które są szybsze. Spójrz na moje rozwiązanie testowe tutaj: [link] (http://lionel.komsa.free.fr/temp/TestDirEnumerate2.zip) –

+0

Być może brakuje mi czegoś, ale dzięki tej metodzie nadal otrzymasz niekompletne wpisy, prawda ? Jeśli mam uprawnienia do wyświetlania folderów A i C, ale nie B, a wyliczenie "Enumerate" to A, to B, a następnie C, zatrzyma się z B i nie będzie się liczyć. –

30

Bardzo zwięzły sposób na uzyskanie rozmiaru folderu w .net 4.0 jest poniżej. Nadal cierpi z powodu konieczności przechodzenia przez rekursywnie wszystkich plików, ale nie ładuje potencjalnie ogromnej ilości nazw plików i jest tylko dwoma liniami kodu.

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

+1 za LINQ'ness! –

+4

Polecam użyć * zamiast *. * ponieważ nazwy plików nie wymagają. – Joe

+4

Podążam za twoim wnioskiem Joe, ale myślę, że nie jest to konieczne, ponieważ system operacyjny Windows będzie pasował do *. * wzorzec w stosunku do nazw plików/folderów, niezależnie od tego, czy zawierają znak "." albo nie. Myślę, że to nie ma znaczenia, ale chciałbym wiedzieć, czy się mylę. Oczywiście, jeśli Mono działa na innym systemie operacyjnym, to może być inaczej. – Kev

-2

Powinieneś ułatwić sobie. Utwórz metodę i podaj położenie katalogu.

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

-G

Powiązane problemy