搜索目录中的所有文件时显示进度

时间:2012-09-12 00:44:19

标签: c# filesystems directoryinfo

我之前问过问题Get all files and directories in specific path fast,以便尽可能快地找到文件。我正在使用该解决方案,以便找到与正则表达式匹配的文件名。

我希望显示一个进度条,因为有一些非常大而慢的硬盘驱动器,它仍然需要大约1分钟才能执行。我在另一个链接上发布的解决方案无法让我知道为了让我显示进度条,还有多少文件丢失了。

我正在考虑的一个解决方案是尝试获取我正在计划遍历的目录的大小。例如,当我右键单击文件夹C:\Users时,我能够估计该目录的大小。如果我能够知道大小,那么我将能够通过添加我找到的每个文件的大小来显示进度。换句话说,progress =(文件大小的当前总和)/目录大小

由于某些原因,我无法有效地获取该目录的大小。

有关堆栈溢出的一些问题使用以下方法:

enter image description here

但请注意,我收到异常但无法枚举文件。我很高兴在我的驱动器上尝试这种方法。

在那张照片上,我试图计算文件数量以显示进度。 我可能无法使用该方法有效获取文件数。当人们问how to get the number of files on a directory并且人们问how the get the size f a directory时,我只是尝试堆栈溢出的一些答案。

2 个答案:

答案 0 :(得分:5)

解决这个问题会让你有几种可能性......

  1. 未显示进度
  2. 使用前期成本计算(如Windows)
  3. 在计算成本时执行操作
  4. 如果速度非常重要,并且您希望使用大型目录树,我会倾向于最后一个选项。我在链接问题Get all files and directories in specific path fast上添加了一个答案,该答案演示了一种比您当前使用的更快的计算文件和大小的方法。要将它组合成选项#3的多线程代码段,可以执行以下操作......

    static void Main()
    {
        const string directory = @"C:\Program Files";
        // Create an enumeration of the files we will want to process that simply accumulates these values...
        long total = 0;
        var fcounter = new CSharpTest.Net.IO.FindFile(directory, "*", true, true, true);
        fcounter.RaiseOnAccessDenied = false;
        fcounter.FileFound +=
            (o, e) =>
                {
                    if (!e.IsDirectory)
                    {
                        Interlocked.Increment(ref total);
                    }
                };
    
        // Start a high-priority thread to perform the accumulation
        Thread t = new Thread(fcounter.Find)
            {
                IsBackground = true, 
                Priority = ThreadPriority.AboveNormal, 
                Name = "file enum"
            };
        t.Start();
    
        // Allow the accumulator thread to get a head-start on us
        do { Thread.Sleep(100); }
        while (total < 100 && t.IsAlive);
    
        // Now we can process the files normally and update a percentage
        long count = 0, percentage = 0;
        var task = new CSharpTest.Net.IO.FindFile(directory, "*", true, true, true);
        task.RaiseOnAccessDenied = false;
        task.FileFound +=
            (o, e) =>
                {
                    if (!e.IsDirectory)
                    {
                        ProcessFile(e.FullPath);
                        // Update the percentage complete...
                        long progress = ++count * 100 / Interlocked.Read(ref total);
                        if (progress > percentage && progress <= 100)
                        {
                            percentage = progress;
                            Console.WriteLine("{0}% complete.", percentage);
                        }
                    }
                };
    
        task.Find();
    }
    

    可以在FindFile class找到FindFile.cs实施。

    根据文件处理任务的成本(上面的ProcessFile函数),您应该看到大量文件的进度非常干净。如果文件处理速度非常快,则可能需要增加枚举开始和处理开始之间的延迟。

    事件参数的类型为FindFile.FileFoundEventArgs,并且是一个可变类,因此请确保不要保留对事件参数的引用,因为它的值会发生变化。

    理想情况下,您需要添加错误处理,并且可能需要中止两个枚举。可以通过在事件参数上设置“CancelEnumeration”来中止枚举。

答案 1 :(得分:3)

由于文件系统如何存储数据,您可能无法提出要求。

这是文件系统限制

无法知道文件夹的总大小,文件夹中的总文件数,而不会枚举文件一个接一个。这些信息都不存储在文件系统中。

这就是为什么Windows在复制具有大量文件的文件夹之前会显示"Calculating space"之类的消息...它实际上是在计算文件夹中有多少文件,并将它们的大小相加以便它可以显示进行真正的复制操作时进度条。 (它还使用这些信息来了解目的地是否有足够的空间来容纳所有被复制的数据)。

此外,当您右键单击文件夹并转到属性时,请注意,计算所有文件并汇总所有文件大小需要一些时间。这是由同样的限制引起的。

要知道文件夹的大小,或文件夹中有多少文件,您必须逐个枚举文件。

快速文件枚举

当然,正如您已经知道的那样,有许多方法可以自己进行枚举......但没有一种方法可以瞬间完成。您可以尝试使用文件系统的USN Journal进行扫描。看看CodePlex中的这个项目:MFT Scanner in VB.NET代码实际上是在C#中...不知道为什么作者说它是VB.NET )...它找到了我的IDE SATA(不是SSD)驱动器中的所有文件都在15秒内完成,并找到了311000个文件。

您必须按路径过滤文件,以便仅返回您正在查看的路径中的文件。但这是工作的简单部分!

希望这对你的项目有所帮助......祝你好运!