【问题标题】:What is the fastest way to calculate a Windows folders size?计算 Windows 文件夹大小的最快方法是什么?
【发布时间】:2016-07-13 12:42:16
【问题描述】:

我需要计算数百个文件夹的大小,有些是 10MB,有些可能是 10GB,我需要一种使用 C# 获取每个文件夹大小的超快速方法。

希望我的最终结果是:

文件夹 1 10.5GB

文件夹 2 230MB

文件夹 3 1.2GB

...

【问题讨论】:

  • 在 C# 中无法做到这一点。 C# 没有访问文件系统的功能。您将不得不使用 .NET Framework 或 Win32 API。
  • 您好 John,我将如何使用 .Net 或 Win32 API 执行此操作?任何想法最快或都慢?
  • @john:我认为“C# 没有访问文件系统的功能”可能被错误地解释,主要是因为 System.IO。我明白你的意思,但对路人来说,这可能意味着其他事情。
  • @RandomNoob:我希望路人把C#和.NET Framework不一样。
  • @John Saunders:这是一个特别悬疑的观点。 OP 已经标记了问题“文件系统”和“.net”。如果我是 VB.net 程序员,我可能会用 VB.net 来表达问题,以便征求用 VB.net 而不是 C# 编写的答案。

标签: c# .net filesystems


【解决方案1】:

添加对 Microsoft Scripting Runtime 的引用并使用:

Scripting.FileSystemObject fso = new Scripting.FileSystemObject();
Scripting.Folder folder = fso.GetFolder([folder path]);
Int64 dirSize = (Int64)folder.Size;

如果您只需要大小,这比递归快得多

【讨论】:

  • 一个包含 900K 文件和 9.5 GB 大小的目录的速度示例:此方法:250 毫秒,递归方法:~15 秒。
  • 深*纳米!这样做,它快了大约 90%!非常精彩!如果我可以多次投票,我会这样做,因为这是一件了不起的事情。关于这个 COM 参考是否有问题的任何 cmets?
  • 我不知道“Microsoft Scripting Runtime”。我搜索了谷歌,但没有找到任何令人满意的答案。您能否帮助我如何添加对“Microsoft Scripting Runtime”的引用?我正在使用 Visual Studio 2013 和 Framework 4.5。
  • 您也可以通过添加引用 > COM > Microsoft Scripting Runtime 添加引用
  • 本地窗口中的大小为dynamic {double} var size = new Scripting.FileSystemObject().GetFolder(path).Size;
【解决方案2】:

好吧,这很糟糕,但是......

使用名为 dirsize.bat 的递归 dos 批处理文件:

@ECHO OFF
IF %1x==x GOTO start
IF %1x==DODIRx GOTO dodir
SET CURDIR=%1
FOR /F "usebackq delims=" %%A IN (`%0 DODIR`) DO SET ANSWER=%%A %CURDIR%
ECHO %ANSWER%
GOTO end
:start
FOR /D %%D IN (*.*) DO CALL %0 "%%D"
GOTO end
:dodir
DIR /S/-C %CURDIR% | FIND "File(s)"
GOTO end
:end

注意:第 5 行最后的“%%A”后面应该有一个制表符,而不是空格。

这是您要查找的数据。它将相当快地处理数千个文件。事实上,它在不到 2 秒的时间内完成了我的整个硬盘驱动器。

像这样dirsize | sort /R /+25 执行文件,以查看首先列出的最大目录。

祝你好运。

【讨论】:

  • 似乎对我不起作用...当针对我的硬盘运行时(cd C:/,然后是dirsize.bat),大约需要一分钟并回显“ECHO 已禁用”9 次。 (大约需要的时间,它不是 SSD 而是混合的,我有 505k 文件和 233k 文件夹)
【解决方案3】:

.Net 中没有简单的方法可以做到这一点;您将不得不遍历每个文件和子目录。 请参阅示例 here 以了解它是如何完成的。

【讨论】:

  • 在 Windows 中似乎也很慢,所以也许没有快速的方法,那么计算 100 或 1000 的大文件夹的大小可能不可行:-(
  • 在阅读了所有我决定不这样做的 cmets 之后,这是一个很好的功能,但开销太大了。谢谢大家。
【解决方案4】:

如果您右键单击一个大目录然后属性,您会看到计算大小需要大量时间...我认为我们无法在这方面击败 MS。您可以做的一件事是索引目录/子目录的大小,如果您要一遍又一遍地计算它们......这将显着提高速度。

您可以使用类似的方法在 C# 中递归计算目录大小

static long DirSize(DirectoryInfo directory)
{
    long size = 0;

    FileInfo[] files = directory.GetFiles();
    foreach (FileInfo file in files)
    {
        size += file.Length;
    }

    DirectoryInfo[] dirs = directory.GetDirectories();

    foreach (DirectoryInfo dir in dirs)
    {
        size += DirSize(dir);
    }

    return size;
}

【讨论】:

    【解决方案5】:

    您可以这样做,但在获取文件夹大小时没有 fast=true 设置,您必须将文件大小相加。

        private static IDictionary<string, long> folderSizes;
    
        public static long GetDirectorySize(string dirName)
        {
            // use memoization to keep from doing unnecessary work
            if (folderSizes.ContainsKey(dirName))
            {
                return folderSizes[dirName];
            }
    
            string[] a = Directory.GetFiles(dirName, "*.*");
    
            long b = 0;
            foreach (string name in a)
            {
                FileInfo info = new FileInfo(name);
                b += info.Length;
            }
    
            // recurse on all the directories in current directory
            foreach (string d in Directory.GetDirectories(dirName))
            {
                b += GetDirectorySize(d);
            }
    
            folderSizes[dirName] = b;
            return b;
        }
    
        static void Main(string[] args)
        {
            folderSizes = new Dictionary<string, long>();
            GetDirectorySize(@"c:\StartingFolder");
            foreach (string key in folderSizes.Keys)
            {
                Console.WriteLine("dirName = " + key + " dirSize = " + folderSizes[key]);
            }
    
            // now folderSizes will contain a key for each directory (starting
            // at c:\StartingFolder and including all subdirectories), and
            // the dictionary value will be the folder size
        }
    

    【讨论】:

    • GetDirectorySize() 的初始调用在哪里?没有这个,代码什么也不做,因为 folderSizes 是空的。
    • 此外,folderSizes 还将包含所有子目录,而 OP 似乎只想要顶级的大小。
    • @JBRWilkinson - 是的,在我的一次编辑中,我不小心拔出了最初的电话。感谢您指出。字典将包含所有结果,但 OP 可以使用他/她需要的结果。
    【解决方案6】:

    Dot Net Pearls 有一个类似于这里描述的方法。令人惊讶的是,System.IO.DirectoryInfo 类没有执行此操作的方法,因为这似乎是一种常见的需求,而且如果不对每个文件系统对象进行本机/托管转换,这样做可能会更快。我确实认为,如果速度是关键,编写一个非托管对象来执行此计算,然后从托管代码中的每个目录调用一次。

    【讨论】:

      【解决方案7】:

      我能找到的 4.0-4.5 框架上计算文件大小及其在磁盘上的数量的最快方法是:

      using System.IO;
      using System.Threading;
      using System.Threading.Tasks;
      
      class FileCounter
      {
        private readonly int _clusterSize;
        private long _filesCount;
        private long _size;
        private long _diskSize;
      
        public void Count(string rootPath)
        {
          // Enumerate files (without real execution of course)
          var filesEnumerated = new DirectoryInfo(rootPath)
                                    .EnumerateFiles("*", SearchOption.AllDirectories);
          // Do in parallel
          Parallel.ForEach(filesEnumerated, GetFileSize);
        }
      
        /// <summary>
        /// Get real file size and add to total
        /// </summary>
        /// <param name="fileInfo">File information</param>
        private void GetFileSize(FileInfo fileInfo)
        {
          Interlocked.Increment(ref _filesCount);
          Interlocked.Add(ref _size, fileInfo.Length);
        }
      }
      
      var fcount = new FileCounter("F:\\temp");
      fcount.Count();
      

      这种方法对我来说似乎是我在 .net 平台上能找到的最好的方法。顺便说一句,如果您需要计算磁盘上的集群大小和实际大小,您可以执行以下操作:

      using System.Runtime.InteropServices;
      
      private long WrapToClusterSize(long originalSize)
          {
              return ((originalSize + _clusterSize - 1) / _clusterSize) * _clusterSize;
          }
      
      private static int GetClusterSize(string rootPath)
          {
              int sectorsPerCluster = 0, bytesPerSector = 0, numFreeClusters = 0, totalNumClusters = 0;
              if (!GetDiskFreeSpace(rootPath, ref sectorsPerCluster, ref bytesPerSector, ref numFreeClusters,
                                    ref totalNumClusters))
              {
                  // Satisfies rule CallGetLastErrorImmediatelyAfterPInvoke.
                  // see http://msdn.microsoft.com/en-us/library/ms182199(v=vs.80).aspx
                  var lastError = Marshal.GetLastWin32Error();
                  throw new Exception(string.Format("Error code {0}", lastError));
              }
              return sectorsPerCluster * bytesPerSector;
          }
      [DllImport(Kernel32DllImport, SetLastError = true)]
          private static extern bool GetDiskFreeSpace(
              string rootPath,
              ref int sectorsPerCluster,
              ref int bytesPerSector,
              ref int numFreeClusters,
              ref int totalNumClusters);
      

      当然你需要在第一个代码部分重写 GetFileSize():

      private long _diskSize;
      private void GetFileSize(FileInfo fileInfo)
          {
              Interlocked.Increment(ref _filesCount);
              Interlocked.Add(ref _size, fileInfo.Length);
              Interlocked.Add(ref _diskSize, WrapToClusterSize(fileInfo.Length));
          }
      

      【讨论】:

        【解决方案8】:

        this link(尽管它是在 Python 中)中有一些来自遇到类似性能问题的人的线索。您可以尝试拨打Win32 API 以查看性能是否有所提高,但最后您会遇到同样的问题:一项任务只能如此快速地完成,并且您必须多次执行该任务,这将花费很多时间。你能详细说明你这样做是为了什么吗?它可能会帮助人们想出一个启发式或一些秘籍来帮助你。如果你经常做这个计算,你会缓存结果吗?

        【讨论】:

          【解决方案9】:

          我很确定这会非常缓慢, 但我会这样写:

          using System.IO;
          
          long GetDirSize(string dir) {
             return new DirectoryInfo(dir)
                .GetFiles("", SearchOption.AllDirectories)
                .Sum(p => p.Length);
          }
          

          【讨论】:

            猜你喜欢
            • 2010-09-16
            • 2011-01-25
            • 1970-01-01
            • 2010-12-31
            • 2017-06-27
            • 2012-07-27
            • 1970-01-01
            • 2011-02-25
            相关资源
            最近更新 更多