2011-10-23 50 views
53

的目录检索文件,我有一个包含支持* .wav格式的近1400万的音频采样目录。从包含大量文件

所有普通的存储,没有子目录。

我想遍历文件,但是当我在该文件夹上使用DirectoryInfo.GetFiles()时,整个应用程序冻结了几分钟!

可以这样做另一种方式?也许读1000,处理它们,然后再拿1000等等?

+0

'的DirectoryInfo。如果您使用网络SAN,GetFiles()'也很糟糕。它锁定所有文件并阻止他人访问最近创建的SAN文件。我们从未找到过无阻塞的解决方案。 – SliverNinja

+0

如果你在一个真正的perf临界点我也会考虑:http://stackoverflow.com/questions/724148/is-there-a-faster-way-to-scan-through-a-directory-recursively-in -net/724184#724184 –

回答

85

您是否试过EnumerateFiles DirectoryInfo类的方法?

由于MSDN说

EnumerateFilesGetFiles方法的区别如下:当您使用 EnumerateFiles,你可以返回整个集合之前开始枚举 FileInfo对象的集合;当您使用GetFiles时,您必须等待FileInfo对象的整个数组返回 ,然后才能访问该数组。因此,当你 许多文件和目录,EnumerateFiles可以更有效地 。

+4

+1有趣。不知道它存在。虽然它确实调用了内部包装在自定义枚举器中的相同API。 –

+0

我的GetFiles方法只返回字符串,而不是FileInfo。 – MrFox

+0

@MrFox '串DIR;' 'Directory.GetFiles' /'Directory.EnumerateFiles'返回字符串 '新DirectoryInfo的(DIR).getFiles' /'新DirectoryInfo的(DIR).EnumerateFiles'返回的FileInfo –

6

使用Win32 Api FindFile功能来做到这一点,而不会阻止应用程序。

您也可以在System.Threading.Task(TPL)中调用Directory.GetFiles以防止UI冻结。

14

您正在受到Windows文件系统本身的限制。当目录中的文件的数量增长到了大量的(和14M远远超出该阈值),访问目录变得慢得令人难以置信。一次读取一个文件或1000个文件并不重要,它只是目录访问。要解决这个

一种方法是创建子目录和文件分裂成组。如果每个目录有1000-5000(猜测但你可以试验实际的数字),那么你应该得到不错的表现打开/创建/删除文件。

这就是为什么如果你看看像Doxygen的,它为每个类创建一个文件的应用程序,他们按照这个方案,把一切都变成2级,其使用随机名的子目录。

+0

+ 1,确切如此。我会补充说,最好是做一个数据库解决方案,或使用适合大量文件的文件系统;如ReiserFS。我不确定一个ReiserFS驱动程序是否适用于Windows。 – Gleno

+0

最好的例子是git,它把对象放在名字是SHA1哈希的前两个字母的文件夹中。 – manojlds

+0

@DXM - 你能提供一些关于这个限制的参考吗?我一直认为NTFS在处理大型目录时没有问题(http://technet.microsoft.com/en-us/library/cc781134(WS.10).aspx关于文件夹中的300k文件),但是浏览器是大减速。 – ligos

40

在.NET 4.0,Directory.EnumerateFiles(...)IEnumerable<string>(而不是Directory.GetFiles(...)string[]),因此它可以流条目,而不是缓冲它们全部;即

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

提及.NET 4的+1,这点很重要 – sll

+0

有趣的一点...关键在于返回类型。 – SliverNinja

1

我打这个问题很多时间访问单个目录中的大文件。子目录是一个不错的选择,但是即使它们不提供太多帮助。我现在要做的就是创建一个索引文件 - 与目录中的所有文件名的文本文件(提供我创造在目录中的文件)。然后我读了索引文件,然后从目录中打开,然后实际的文件进行处理

4

享受。

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