【问题标题】:Show progress when searching all files in a directory搜索目录中的所有文件时显示进度
【发布时间】:2012-09-12 00:44:19
【问题描述】:

我之前问过Get all files and directories in specific path fast 的问题是为了尽快找到文件。我正在使用该解决方案来查找与正则表达式匹配的文件名。

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

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

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

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

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

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

【问题讨论】:

  • 您的程序没有足够的权限访问该目录。以管理员权限运行程序,或在应用清单中要求它们。右键单击->以管理员身份运行。
  • 即使我以管理员身份运行,C 盘上仍有其他文件无法访问。
  • 例如尝试以管理员身份访问文件夹c:\Documents and Settings。我认为Directory.EnumerateFiles 方法缺少处理这个问题的方法。

标签: c# filesystems directoryinfo


【解决方案1】:

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

  1. 不显示进度
  2. 使用预付费用进行计算(如 Windows)
  3. 执行操作同时计算成本

如果速度很重要,并且您期望大型目录树,我会倾向于这些选项中的最后一个。我在链接的问题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”来中止枚举。

【讨论】:

  • 哇,您的解决方案要快得多。为什么stackoverflow.com/questions/6061957/…(我使用的那个)的算法会返回更多的文件。事实上,它返回 401,000 个文件,而你的算法在对我的 C:\ 驱动器执行此操作时返回 207,000 个。
  • 我忘了联锁计数器,我数了 327180 个文件。仍然比其他算法少得多。为什么?
  • 你确定区别不在于另一个例程列出目录和文件吗?如果您同时计算目录和文件,请意识到 FileFind 类不报告名为“。”的目录。或“..”出于显而易见的原因。如果您好奇,请将所有路径写入两个文本文件并进行比较。我进行了一些测试,发现文件计数完全匹配。
  • 是的,我没有计算名称为 ... 的目录。对路径 `C:` 执行此操作时,我只会得到更少的文件。我想知道为什么。我很快就会对 to 方法进行比较,看看哪些文件没有被包含...
  • 告诉我你发现了什么...你也可以使用我的 SO 用户名“roger@”给我发电子邮件,“csharptest.net”。
【解决方案2】:

由于文件系统存储数据的方式,您的要求可能无法实现。

这是文件系统限制

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

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

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

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

快速文件枚举

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

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

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

【讨论】:

  • 另外,枚举文件可能存在权限问题(如上所述)
  • 是的,有权限问题,我是我电脑的管理员。要使用此方法,用户必须具有管理权限,正如CreateFile API 方法的文档中所说,关于打开物理磁盘驱动器或卷:msdn.microsoft.com/en-us/library/windows/desktop/…
猜你喜欢
  • 1970-01-01
  • 2019-11-17
  • 2019-11-17
  • 1970-01-01
  • 1970-01-01
  • 2021-01-06
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
相关资源
最近更新 更多