6

我之前问过这个问题:快速获取特定路径中的所有文件和目录,以便尽可能快地找到文件。我正在使用该解决方案来查找与正则表达式匹配的文件名。

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

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

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

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

在此处输入图像描述

但请注意,我得到一个异常并且无法枚举文件。我很想在我的 C 盘上尝试这种方法。

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

4

2 回答 2

6

解决这个问题会给你留下几种可能性之一......

  1. 不显示进度
  2. 使用前期成本进行计算(如 Windows)
  3. 在计算成本的同时执行操作

如果速度如此重要并且您期望大型目录树,我会倾向于这些选项中的最后一个。我已经在链接的问题上添加了一个答案,快速获取特定路径中的所有文件和目录,这展示了一种比您当前使用的更快的计算文件和大小的方法。要将其组合到选项 #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 类的实现可以在FindFile.cs找到。

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

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

理想情况下,您将希望添加错误处理以及可能中止两个枚举的能力。可以通过在事件参数上设置“CancelEnumeration”来中止枚举。

于 2012-09-17T18:28:20.630 回答
4

由于文件系统如何存储它的数据,您所问的可能是不可能的。

这是文件系统限制

如果不逐个枚举文件,就无法知道文件夹的总大小,也无法知道文件夹内的文件总数。这些信息都不存储在文件系统中。

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

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

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

快速文件枚举

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

您必须按路径过滤文件,以便只返回您正在查找的路径内的文件。但这是工作中最容易的部分!

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

于 2012-09-17T16:58:10.463 回答