2011-10-23 17 views
53

Tengo un directorio que contiene casi 14,000,000 de muestras de audio en formato * .wav.Recuperando archivos del directorio que contiene gran cantidad de archivos

Todo el almacenamiento sin formato, sin subdirectorios.

Quiero recorrer los archivos, pero cuando uso DirectoryInfo.GetFiles() en esa carpeta toda la aplicación se congela durante unos minutos.

¿Se puede hacer esto de otra manera? Tal vez leer 1000, procesarlos, luego tomar el próximo 1000 y así sucesivamente?

+0

'DirectoryInfo.GetFiles() 'también es horrible si está utilizando una SAN de red. Bloquea todos los archivos y bloquea el acceso de otros a los archivos SAN recientemente creados. Nunca encontramos una resolución sin bloqueo. – SliverNinja

+0

si se encuentra en un punto crítico real, también lo consideraría: http://stackoverflow.com/questions/724148/is-there-a-faster-way-to-scan-through-a-directory-recursively-in -net/724184 # 724184 –

Respuesta

85

¿Has probado el método EnumerateFiles de la clase DirectoryInfo?

Como MSDN Dice

El EnumerateFiles y GetFiles métodos difieren de la siguiente manera: Cuando se utiliza EnumerateFiles, puede empezar a enumerar la colección de objetos antes de FileInfo se devuelve toda la colección; cuando usa GetFiles, debe esperar a que se devuelva todo el conjunto de FileInfo objetos a para poder acceder a la matriz. Por lo tanto, cuando está trabajando con muchos archivos y directorios, EnumerateFiles puede ser más eficiente.

+4

+1 Interesante. No sabía que existía. Aunque llama a las mismas API internamente envueltas en un enumerador personalizado. –

+0

Mi método GetFiles solo devuelve cadena, no FileInfo. – MrFox

+0

@MrFox 'dir cadena;' ' Directory.GetFiles' /' Directory.EnumerateFiles' cadena de retorno 'nueva DirectoryInfo (dir) .getFiles' /' nueva DirectoryInfo (dir) .EnumerateFiles' volver FileInfo –

6

Use las funciones de Win32 Api FindFile para hacerlo sin bloquear la aplicación.

También puede llamar a Directory.GetFiles en un System.Threading.Task (TPL) para evitar que se congele su UI.

14

Usted está llegando a la limitación del sistema de archivos de Windows en sí. Cuando la cantidad de archivos en un directorio crece hasta convertirse en un número grande (y 14M es mucho más allá de ese umbral), acceder al directorio se vuelve increíblemente lento. Realmente no importa si lee un archivo a la vez o 1000, es solo acceso al directorio.

Una forma de resolver esto es crear subdirectorios y separar sus archivos en grupos. Si cada directorio tiene 1000-5000 (supongo, pero puede experimentar con números reales), entonces debe obtener un rendimiento decente abriendo/creando/eliminando archivos.

Es por eso que si observas aplicaciones como Doxygen, que crea un archivo para cada clase, siguen este esquema y ponen todo en 2 niveles de subdirectorios que usan nombres aleatorios.

+0

+ 1, exactamente así. Agregaría que es mejor hacer una solución de DB, o usar un sistema de archivos adecuado para una gran cantidad de archivos; como ReiserFS. No estoy seguro si un controlador ReiserFS está disponible para Windows o no. – Gleno

+0

El mejor ejemplo es git que coloca los objetos en carpetas cuyo nombre son las dos primeras letras del hash SHA1. – manojlds

+0

@DXM: ¿me puede dar algunas referencias sobre esta limitación? Siempre pensé que NTFS no tenía problemas para manejar directorios grandes (http://technet.microsoft.com/en-us/library/cc781134(WS.10).aspx habla de 300k archivos en una carpeta), pero el explorador era la gran ralentización. – ligos

40

En .NET 4.0, Directory.EnumerateFiles(...) es IEnumerable<string> (en lugar de la string[] de Directory.GetFiles(...)), por lo que puede transmitir las entradas en lugar de amortiguar todos ellos; es decir,

foreach(var file in Directory.EnumerateFiles(path)) { 
    // ... 
} 
+3

+1 por mencionar .NET 4, este es el punto importante – sll

+0

Dato interesante ... la clave está en el tipo de devolución. – SliverNinja

1

Me tocó este problema de acceso a archivos grandes en un único directorio muchas veces. Los subdirectorios son una buena opción, pero a veces incluso no ofrecen mucha ayuda a veces. Lo que hago ahora es crear un archivo de índice, un archivo de texto con los nombres de todos los archivos del directorio (siempre que esté creando archivos en ese directorio). Luego leo el archivo de índice y luego abro el archivo actual del directorio para procesar

4

Disfrutar.

public List<string> LoadPathToAllFiles(string pathToFolder, int numberOfFilesToReturn) 
    { 
     var DirInfo = new DirectoryInfo(pathToFolder); 
     var firstFiles = DirInfo.EnumerateFiles().Take(numberOfFilesToReturn).ToList(); 
     return firstFiles.Select(l => l.FullName).ToList(); 
    } 
Cuestiones relacionadas