2009-07-13 15 views
13

Quiero ser capaz de obtener el tamaño de uno de los directorios locales usando C#. Estoy tratando de evitar lo siguiente (pseudo como código), aunque en el peor de los casos voy a tener que conformarse con esto:¿Cómo obtengo un tamaño de directorio (archivos en el directorio) en 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; 
    } 

Básicamente, hay un paseo() disponible en alguna parte para que pueda caminar a través del árbol de directorios? Lo que salvaría la recursión de pasar por cada subdirectorio.

Respuesta

18

Si usa Directory.GetFiles puede hacer una búsqueda recursiva (usando SearchOption.AllDirectories), pero esto es un poco escamoso de todos modos (especialmente si no tiene acceso a uno de los subdirectorios) - y podría implicar un gran single array regresando (advertencia klaxon ...).

Estaría contento con el enfoque de recursión a menos que pudiera mostrar (a través de la creación de perfiles) un cuello de botella; y luego probablemente cambie a (un solo nivel) Directory.GetFiles, usando un Queue<string> para emular la recursión.

Tenga en cuenta que .NET 4.0 presenta algunos métodos de listado de archivos/directorios basados ​​en enumerador que ahorran en las grandes matrices.

+0

Gracias por eso :) – ThePower

0

He estado buscando hace un tiempo para una función como la que pides y de lo que he encontrado en Internet y en los foros de MSDN, no existe tal función.

La manera recursiva es la única que encontré para obtener el tamaño de una Carpeta considerando todos los archivos y subcarpetas que contiene.

4

Se podía ocultar su recursividad detrás de un método de extensión (para evitar los problemas de Marc ha puesto de manifiesto con los GetFiles() método):

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

(Es posible que desee añadir un poco de manipulación para esto para las carpetas protegidas excepción etc.)

continuación:

int totalSize = 0; 

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

Básicamente el mismo código, pero quizás un poco más ordenado ...

6

Aquí mi .NET 4.0 enfoque

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 aún mejor:

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

Como Gabriel señaló esta se producirá un error si tiene un directorio restringido bajo la searchDirectory!

+1

pequeño error tipográfico en la última línea de código, deben ser: // obtener el tamaño de todos los archivos de larga suma = (a partir de archivos en los archivos dejó fileinfo = new FileInfo (archivo) seleccione fileInfo.Length) .Sum(); –

+1

Y si tiene un directorio restringido debajo de searchDirectory, ¡fallará! Para ver esa resolución en una versión futura del marco: https://connect.microsoft.com/VisualStudio/feedback/details/512171/directory-enumeratedirectory-etc-unusable-due-to-frequent-unauthorizedaccessexceptions-even-runas- administrador –

+0

Gracias por sus comentarios Gabriel. He corregido el error tipográfico e incluido la posible advertencia de falla. – flayn

3

Primero, perdone mi inglés pobre; o) Tuve un problema que me llevó a esta página: enumerar archivos de un directorio y sus subdirectorios sin bloquear en una excepción no autorizada y, como el nuevo método de .Net 4 DirectoryInfo .Enumerar ..., obtener el primer resultado antes del final de toda la consulta.

Con la ayuda de varios ejemplos que se encuentran aquí y allá en la web, que finalmente escribir este método:

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

utilizo una cola y un método recursivo para mantener el orden tradicional (contenido del directorio y contenido del primer subdirectorio y sus propios subdirectorios y luego el contenido del segundo ...). El parámetro "handleExceptionAccess" es solo una llamada a función cuando se lanza una excepción con un directorio; la función debe devolver verdadero para indicar que la excepción debe ser ignorada.

Con esta méthode, puede escribir:

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

Y aquí estamos: todos excepto cuando se trata de enumerar un directorio será ignorar!

Esperanza esta ayuda

Lionel

PD: por una razón que no puedo explicar, mi método es más rápido que el marco de un 4 ...

PPS: se puede conseguir mi soluciones de prueba con la fuente de esos métodos: aquí TestDirEnumerate. Escribo EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (uso una cola para evitar la recursión) y EnumerateFiles_NonRecursive_TraditionalOrder (uso una pila de cola para evitar la recursión y mantener el orden tradicional). Mantener esos 3 métodos no tiene interés, los escribo solo para probar el mejor. Pienso guardar solo el último. También escribí el equivalente para EnumerateFileSystemInfos y EnumerateDirectories.

+0

Hice un error en esta versión porque busco sudirectories con el searchPattern. Entonces, si un subdirectorio no coincide con searchPattern, no lo exploro ... [br] Escribí una nueva versión para corregir esto: pero debo hacer dos solicitudes en el directorio: una con searchPattern y otra sin searchPattern ==> el rendimiento no es tan bueno como antes. –

+0

Así que agrego una versión sin el patrón de búsqueda que son más rápidos. Mire mi solución de prueba aquí: [link] (http://lionel.komsa.free.fr/temp/TestDirEnumerate2.zip) –

+0

Quizás me esté faltando algo, pero con este método todavía obtendrá listados incompletos, a la derecha ? Si estoy autorizado para ver las carpetas A y C pero no B, y 'Enumerar' examina A, luego B, luego C, se detendrá con B y no contará C. –

30

Una forma muy sucinta de obtener un tamaño de carpeta en .NET 4.0 es más abajo. Todavía adolece de la limitación de tener que atravesar todos los archivos de forma recursiva, pero no carga una gran variedad de nombres de archivos y solo tiene dos líneas de código.

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

+1 para LINQ'ness! –

+4

Le recomiendo que use * en lugar de *. * porque los nombres de archivo no requieren a. – Joe

+4

Sigo tu razonamiento Joe, pero creo que es innecesario porque el sistema operativo Windows coincidirá con el *. * patrón contra nombres de archivos/carpetas sin importar si contienen un '.' o no. Creo que no hace ninguna diferencia, pero estaría interesado en saber si estoy equivocado. Por supuesto, si fue Mono corriendo en un sistema operativo diferente, entonces bien podría ser diferente. – Kev

-2

Debe hacerlo usted mismo. Haz un método y pasa a través de la ubicación del directorio.

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

-G

Cuestiones relacionadas