代码之家  ›  专栏  ›  技术社区  ›  Steve Wranovsky

在.NET中计算目录大小的最佳方法是什么?

  •  58
  • Steve Wranovsky  · 技术社区  · 16 年前

    我编写了以下例程,以手动遍历目录并在C#/.NET中计算其大小:

    
    protected static float CalculateFolderSize(string folder)
    {
        float folderSize = 0.0f;
        try
        {
            //Checks if the path is valid or not
            if (!Directory.Exists(folder))
                return folderSize;
            else
            {
                try
                {
                    foreach (string file in Directory.GetFiles(folder))
                    {
                        if (File.Exists(file))
                        {
                            FileInfo finfo = new FileInfo(file);
                            folderSize += finfo.Length;
                        }
                    }
    
                    foreach (string dir in Directory.GetDirectories(folder))
                        folderSize += CalculateFolderSize(dir);
                }
                catch (NotSupportedException e)
                {
                    Console.WriteLine("Unable to calculate folder size: {0}", e.Message);
                }
            }
        }
        catch (UnauthorizedAccessException e)
        {
            Console.WriteLine("Unable to calculate folder size: {0}", e.Message);
        }
        return folderSize;
    }
    
    

    我有一个应用程序在大量文件夹中重复运行此例程。我想知道是否有更有效的方法来计算.NET文件夹的大小?我没有看到框架中的任何具体内容。我应该使用P/Invoke和Win32 API吗?在.NET中计算文件夹大小最有效的方法是什么?

    18 回复  |  直到 16 年前
        1
  •  78
  •   Alexandre Pepin    9 年前

    不,这看起来像 recommended way 要计算目录大小,请使用以下相关方法:

    public static long DirSize(DirectoryInfo d) 
    {    
        long size = 0;    
        // Add file sizes.
        FileInfo[] fis = d.GetFiles();
        foreach (FileInfo fi in fis) 
        {      
            size += fi.Length;    
        }
        // Add subdirectory sizes.
        DirectoryInfo[] dis = d.GetDirectories();
        foreach (DirectoryInfo di in dis) 
        {
            size += DirSize(di);   
        }
        return size;  
    }
    

    Console.WriteLine("The size is {0} bytes.", DirSize(new DirectoryInfo(targetFolder));
    

    哪里 targetFolder 要计算的文件夹大小。

        2
  •  35
  •   Trikaldarshiii    5 年前
    DirectoryInfo dirInfo = new DirectoryInfo(@strDirPath);
    long dirSize = await Task.Run(() => dirInfo.EnumerateFiles( "*", SearchOption.AllDirectories).Sum(file => file.Length));
    
        3
  •  26
  •   Mike Thompson    16 年前

    我不相信有一个Win32 API来计算一个目录所消耗的空间,尽管在这一点上我需要纠正。如果有的话,我会假设Explorer会使用它。如果在资源管理器中获得大目录的属性,则获得文件夹大小所需的时间与它包含的文件/子目录的数量成正比。

        4
  •  16
  •   Grozz    11 年前
    public static long DirSize(DirectoryInfo dir)
    {
        return dir.GetFiles().Sum(fi => fi.Length) +
               dir.GetDirectories().Sum(di => DirSize(di));
    }
    
        5
  •  15
  •   Community CDub    4 年前

    真正的问题是, 你打算用这个尺码做什么


    • 逻辑上

    • “有效数据长度”,等于第一个字节的偏移量 它实际上并没有被存储 .
      这始终小于或等于“文件结尾”,并且是群集大小的倍数。
      例如,1 GB文件的有效数据长度可以为1 MB。如果要求Windows读取前8MB,它将读取前1MB,并假装其余数据在那里,将其返回为零。

    • 文件的“分配大小”。这始终大于或等于“文件结尾”。
      这是操作系统为文件分配的群集数乘以群集大小。

    • 文件的“压缩大小”,仅对压缩(和稀疏?)文件有效。
      它等于一个簇的大小乘以卷上所包含的簇的数量 实际分配
      对于非压缩和非稀疏文件,没有“压缩大小”的概念;您将使用“分配大小”来代替。

    你的 问题是像这样的“文件” C:\Foo 实际上可以有多个 溪流 大量的数据。
    这个名字只是指 违约 流动一个文件可能有 候补 C:\Foo:Bar

    你的 第三 问题是“文件”可以 (“硬链接”)。
    例如 C:\Windows\notepad.exe C:\Windows\System32\notepad.exe 是两个 对于 相同的 任何 任何

    你的 问题是,“文件”(或目录)实际上可能不是文件(或目录):
    这可能是一个错误 (一个“符号链接”或“重分析点”)到其他文件(或目录)。

    你的 是因为有一些“过滤”驱动程序可以生成某些文件或目录 就像实际的文件或目录,即使它们不是。例如,可以使用名为ImageX的工具将Microsoft的WIM图像文件(经过压缩)装载到文件夹中,以及

    你的 第六 问题是每个文件都需要元数据。
    例如,同一个文件有10个名称需要更多元数据,这需要空间。如果文件名很短,那么10个文件名可能和1个文件名一样便宜——如果文件名很长,那么多个文件名可能会占用更多的磁盘空间 . (具有多条流的同一故事等)

        6
  •  11
  •   Ahmed Sabry    7 年前
    var size = new DirectoryInfo("E:\\").GetDirectorySize();
    

    下面是这个扩展方法背后的代码

    public static long GetDirectorySize(this System.IO.DirectoryInfo directoryInfo, bool recursive = true)
    {
        var startDirectorySize = default(long);
        if (directoryInfo == null || !directoryInfo.Exists)
            return startDirectorySize; //Return 0 while Directory does not exist.
    
        //Add size of files in the Current Directory to main size.
        foreach (var fileInfo in directoryInfo.GetFiles())
            System.Threading.Interlocked.Add(ref startDirectorySize, fileInfo.Length);
    
        if (recursive) //Loop on Sub Direcotries in the Current Directory and Calculate it's files size.
            System.Threading.Tasks.Parallel.ForEach(directoryInfo.GetDirectories(), (subDirectory) =>
        System.Threading.Interlocked.Add(ref startDirectorySize, GetDirectorySize(subDirectory, recursive)));
    
        return startDirectorySize;  //Return full Size of this Directory.
    }
    
        7
  •  6
  •   Konstantin K.    9 年前

    看起来,以下方法比递归函数执行任务更快:

    long size = 0;
    DirectoryInfo dir = new DirectoryInfo(folder);
    foreach (FileInfo fi in dir.GetFiles("*.*", SearchOption.AllDirectories))
    {
       size += fi.Length;
    }
    

    一个简单的控制台应用程序测试表明,这个循环对文件求和的速度比递归函数快,并且提供了相同的结果。您可能希望使用LINQ方法(如Sum())来缩短此代码。

        8
  •  5
  •   Alex    11 年前

    更快!添加COM引用“Windows脚本主机对象…”

    public double GetWSHFolderSize(string Fldr)
        {
            //Reference "Windows Script Host Object Model" on the COM tab.
            IWshRuntimeLibrary.FileSystemObject FSO = new     IWshRuntimeLibrary.FileSystemObject();
            double FldrSize = (double)FSO.GetFolder(Fldr).Size;
            Marshal.FinalReleaseComObject(FSO);
            return FldrSize;
        }
    private void button1_Click(object sender, EventArgs e)
            {
                string folderPath = @"C:\Windows";
            Stopwatch sWatch = new Stopwatch();
    
            sWatch.Start();
            double sizeOfDir = GetWSHFolderSize(folderPath);
            sWatch.Stop();
            MessageBox.Show("Directory size in Bytes : " + sizeOfDir + ", Time: " + sWatch.ElapsedMilliseconds.ToString());
              }
    
        9
  •  5
  •   miken32 Amit D    8 年前

    这个解决方案非常有效。

    Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
    
        10
  •  4
  •   Rodolfo G.    15 年前

    直到最近,我一直在摆弄VS2008和LINQ,这个紧凑而简短的方法对我来说非常有用(例如在VB.NET中;当然需要LINQ/.NET FW 3.5+):

    Dim size As Int64 = (From strFile In My.Computer.FileSystem.GetFiles(strFolder, _
                  FileIO.SearchOption.SearchAllSubDirectories) _
                  Select New System.IO.FileInfo(strFile).Length).Sum()
    

    我不是C#专家,但您可以在C上添加我的命名空间# this way .

    我认为这种获得文件夹大小的方法不仅比郝的描述的方法更短更现代 link

        11
  •  4
  •   MasterMastic    11 年前

    这是计算目录大小的最佳方法。只有另一种方法仍然使用递归,但更易于使用,并且没有那么灵活。

    float folderSize = 0.0f;
    FileInfo[] files = Directory.GetFiles(folder, "*", SearchOption.AllDirectories);
    foreach(FileInfo file in files) folderSize += file.Length;
    
        12
  •  4
  •   Scott Stafford    10 年前

    我使用相同的计数原则扩展了@Hao的答案,但支持更丰富的数据返回,因此您可以返回大小、递归大小、目录计数和递归目录计数,N个级别。

    public class DiskSizeUtil
    {
        /// <summary>
        /// Calculate disk space usage under <paramref name="root"/>.  If <paramref name="levels"/> is provided, 
        /// then return subdirectory disk usages as well, up to <paramref name="levels"/> levels deep.
        /// If levels is not provided or is 0, return a list with a single element representing the
        /// directory specified by <paramref name="root"/>.
        /// </summary>
        /// <returns></returns>
        public static FolderSizeInfo GetDirectorySize(DirectoryInfo root, int levels = 0)
        {
            var currentDirectory = new FolderSizeInfo();
    
            // Add file sizes.
            FileInfo[] fis = root.GetFiles();
            currentDirectory.Size = 0;
            foreach (FileInfo fi in fis)
            {
                currentDirectory.Size += fi.Length;
            }
    
            // Add subdirectory sizes.
            DirectoryInfo[] dis = root.GetDirectories();
    
            currentDirectory.Path = root;
            currentDirectory.SizeWithChildren = currentDirectory.Size;
            currentDirectory.DirectoryCount = dis.Length;
            currentDirectory.DirectoryCountWithChildren = dis.Length;
            currentDirectory.FileCount = fis.Length;
            currentDirectory.FileCountWithChildren = fis.Length;
    
            if (levels >= 0)
                currentDirectory.Children = new List<FolderSizeInfo>();
    
            foreach (DirectoryInfo di in dis)
            {
                var dd = GetDirectorySize(di, levels - 1);
                if (levels >= 0)
                    currentDirectory.Children.Add(dd);
    
                currentDirectory.SizeWithChildren += dd.SizeWithChildren;
                currentDirectory.DirectoryCountWithChildren += dd.DirectoryCountWithChildren;
                currentDirectory.FileCountWithChildren += dd.FileCountWithChildren;
            }
    
            return currentDirectory;
        }
    
        public class FolderSizeInfo
        {
            public DirectoryInfo Path { get; set; }
            public long SizeWithChildren { get; set; }
            public long Size { get; set; }
            public int DirectoryCount { get; set; }
            public int DirectoryCountWithChildren { get; set; }
            public int FileCount { get; set; }
            public int FileCountWithChildren { get; set; }
            public List<FolderSizeInfo> Children { get; set; }
        }
    }
    
        13
  •  3
  •   Rezo Megrelidze    10 年前
    public static long GetDirSize(string path)
    {
        try
        {
            return Directory.EnumerateFiles(path).Sum(x => new FileInfo(x).Length)  
                +
                   Directory.EnumerateDirectories(path).Sum(x => GetDirSize(x));
        }
        catch
        {
            return 0L;
        }
    }
    
        14
  •  2
  •   Jonathan C Dickinson    16 年前

    就最好的算法而言,你可能是对的。我建议您解开递归函数,使用自己的堆栈(记住,在.Net 2.0+应用程序中,堆栈溢出是世界末日,IIRC无法捕获异常)。

    最重要的是,如果您在任何形式的UI中使用它,请将其放在工作线程上,该线程向UI线程发送更新信号。

        15
  •  2
  •   Community CDub    8 年前

    为了提高性能,可以使用任务并行库(TPL)。 以下是一个很好的示例: Directory file size calculation - how to make it faster?

        16
  •  2
  •   Bernhard    5 年前

    Trikaldarshi单线解决方案的替代方案。(它避免了构造FileInfo对象)

    long sizeInBytes = Directory.EnumerateFiles("{path}","*", SearchOption.AllDirectories).Sum(fileInfo => new FileInfo(fileInfo).Length);
    
        17
  •  2
  •   Bernhard    5 年前
    Directory.GetFiles(@"C:\Users\AliBayat","*",SearchOption.AllDirectories)
    .Select (d => new FileInfo(d))
    .Select (d => new { Directory = d.DirectoryName,FileSize = d.Length} )
    .ToLookup (d => d.Directory )
    .Select (d => new { Directory = d.Key,TotalSizeInMB =Math.Round(d.Select (x =>x.FileSize)
    .Sum () /Math.Pow(1024.0,2),2)})
    .OrderByDescending (d => d.TotalSizeInMB).ToList();
    

    使命感 GetFiles 具有 SearchOption.AllDirectories 返回所有文件中所有文件的全名 subdirectories 指定目录的。操作系统以字节表示文件的大小。可以从文件的长度属性中检索文件大小。将其除以1024(提升到2的幂)可以得到文件的大小(以兆字节为单位)。因为一个目录/文件夹可以包含许多文件, d.Select(x => x.FileSize) 返回以MB为单位的文件大小集合。最后一次呼叫 Sum()

    更新:过滤器任务=” .

        18
  •  1
  •   ladybug    8 年前

    我想到的最快的方法是使用带有SearchOption.AllDirectories的EnumerateFiles。此方法还允许在浏览文件和计算大小时更新UI。长路径名不会导致任何问题,因为不会尝试为长路径名创建FileInfo或DirectoryInfo。在枚举文件时,即使文件名很长,只要起始目录名不太长,枚举文件返回的FileInfo也不会引起问题。未经授权的访问仍然存在问题。

        private void DirectoryCountEnumTest(string sourceDirName)
        {
            // Get the subdirectories for the specified directory.
            long dataSize = 0;
            long fileCount = 0;
            string prevText = richTextBox1.Text;
    
            if (Directory.Exists(sourceDirName))
            {
                DirectoryInfo dir = new DirectoryInfo(sourceDirName);
                foreach (FileInfo file in dir.EnumerateFiles("*", SearchOption.AllDirectories))
                {
                    fileCount++;
                    try
                    {
                        dataSize += file.Length;
                        richTextBox1.Text = prevText + ("\nCounting size: " + dataSize.ToString());
                    }
                    catch (Exception e)
                    {
                        richTextBox1.AppendText("\n" + e.Message);
                    }
                }
                richTextBox1.AppendText("\n files:" + fileCount.ToString());
            }
        }
    
        19
  •  1
  •   gbro3n    6 年前

    此.NET核心命令行应用程序在此计算给定路径的目录大小:

    https://github.com/garethrbrown/folder-size

    关键的方法是递归地检查子目录以得出总大小。

    private static long DirectorySize(SortDirection sortDirection, DirectoryInfo directoryInfo, DirectoryData directoryData)
    {
            long directorySizeBytes = 0;
    
            // Add file sizes for current directory
    
            FileInfo[] fileInfos = directoryInfo.GetFiles();
    
            foreach (FileInfo fileInfo in fileInfos)
            {
                directorySizeBytes += fileInfo.Length;
            }
    
            directoryData.Name = directoryInfo.Name;
    
            directoryData.SizeBytes += directorySizeBytes;
    
            // Recursively add subdirectory sizes
    
            DirectoryInfo[] subDirectories = directoryInfo.GetDirectories();
    
            foreach (DirectoryInfo di in subDirectories)
            {
                var subDirectoryData = new DirectoryData(sortDirection);
    
                directoryData.DirectoryDatas.Add(subDirectoryData);
    
                directorySizeBytes += DirectorySize(sortDirection, di, subDirectoryData);
            }
    
            directoryData.SizeBytes = directorySizeBytes;
    
            return directorySizeBytes;
        }
    }
    
        20
  •  1
  •   SLdragon    5 年前

    计算目录大小的多线程示例 Microsoft Docs ,哪个更快

    using System;
    using System.IO;
    using System.Threading;
    using System.Threading.Tasks;
    
    public class Example
    {
       public static void Main()
       {
          long totalSize = 0;
    
          String[] args = Environment.GetCommandLineArgs();
          if (args.Length == 1) {
             Console.WriteLine("There are no command line arguments.");
             return;
          }
          if (! Directory.Exists(args[1])) {
             Console.WriteLine("The directory does not exist.");
             return;
          }
    
          String[] files = Directory.GetFiles(args[1]);
          Parallel.For(0, files.Length,
                       index => { FileInfo fi = new FileInfo(files[index]);
                                  long size = fi.Length;
                                  Interlocked.Add(ref totalSize, size);
                       } );
          Console.WriteLine("Directory '{0}':", args[1]);
          Console.WriteLine("{0:N0} files, {1:N0} bytes", files.Length, totalSize);
       }
    }
    // The example displaysoutput like the following:
    //       Directory 'c:\windows\':
    //       32 files, 6,587,222 bytes
    

    String[] files = Directory.GetFiles(args[1]);
    

    String[] files = Directory.GetFiles(args[1], "*", SearchOption.AllDirectories);
    
    
        21
  •  0
  •   Ken Tseng    5 年前

    我试图改变样本(Alexandre Pepin和hao的回答)

    原样

        private long GetDirectorySize(string dirPath)
        {
            if (Directory.Exists(dirPath) == false)
            {
                return 0;
            }
    
            DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
    
            long size = 0;
    
            // Add file sizes.
            FileInfo[] fis = dirInfo.GetFiles();
            foreach (FileInfo fi in fis)
            {
                size += fi.Length;
            }
    
            // Add subdirectory sizes.
            DirectoryInfo[] dis = dirInfo.GetDirectories();
            foreach (DirectoryInfo di in dis)
            {
                size += GetDirectorySize(di.FullName);
            }
    
            return size;
        }
    

        private long GetDirectorySize2(string dirPath)
        {
            if (Directory.Exists(dirPath) == false)
            {
                return 0;
            }
    
            DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
    
            long size = 0;
    
            // Add file sizes.
            IEnumerable<FileInfo> fis = dirInfo.EnumerateFiles("*.*", SearchOption.AllDirectories);
            foreach (FileInfo fi in fis)
            {
                size += fi.Length;
            }
    
            return size;
        }
    

    最后,您可以检查结果

            // ---------------------------------------------
            // size of directory
            using System.IO;
    
            string log1Path = @"D:\SampleDirPath1";
            string log2Path = @"D:\SampleDirPath2";
            string log1DirName = Path.GetDirectoryName(log1Path);
            string log2DirName = Path.GetDirectoryName(log2Path);
            long log1Size = GetDirectorySize(log1Path);
            long log2Size = GetDirectorySize(log2Path);
            long log1Size2 = GetDirectorySize2(log1Path);
            long log2Size2 = GetDirectorySize2(log2Path);
    
            Console.WriteLine($@"{log1DirName} Size: {SizeSuffix(log1Size)}, {SizeSuffix(log1Size2)}
            {log2DirName} Size: {SizeSuffix(log2Size)}, {SizeSuffix(log2Size2)}");
    

    这是SizeSuffix函数

        private static readonly string[] SizeSuffixes =
                   { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
    
        /// <summary>
        /// Size Display
        /// </summary>
        /// <param name="value">bytes 數值</param>
        /// <param name="decimalPlaces">小數位數</param>
        /// <returns></returns>
        public static string SizeSuffix(Int64 value, int decimalPlaces = 2)
        {
            if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
            if (value < 0) { return "-" + SizeSuffix(-value); }
            if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
    
            // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
            int mag = (int)Math.Log(value, 1024);
    
            // 1L << (mag * 10) == 2 ^ (10 * mag) 
            // [i.e. the number of bytes in the unit corresponding to mag]
            decimal adjustedSize = (decimal)value / (1L << (mag * 10));
    
            // make adjustment when the value is large enough that
            // it would round up to 1000 or more
            if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
            {
                mag += 1;
                adjustedSize /= 1024;
            }
    
            return string.Format("{0:n" + decimalPlaces + "} {1}",
                adjustedSize,
                SizeSuffixes[mag]);
        }
    
        22
  •  -3
  •   Tono Nam    6 年前

    我知道这不是一个.net解决方案,但无论如何,它来了。对于那些拥有Windows10并且想要更快解决方案的人来说,它可能很方便。例如,如果在命令提示符下运行此命令,或按 winKey + R

    bash -c "du -sh /mnt/c/Users/; sleep 5"    
    

    sleep 5 这样您就有时间查看结果,而窗口不会关闭

    enter image description here

    h 代表人类可读性。

    那就做点像 Processes.Start("bash",... arguments) 这不是确切的代码,但你明白了。