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

Ste*_*sky 69 .net c# windows

我已编写以下例程来手动遍历目录并在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中计算文件夹大小的最有效方法是什么?

hao*_*hao 58

不,这看起来像计算目录大小的推荐方法,相关方法包括如下:

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;  
}
Run Code Online (Sandbox Code Playgroud)

您可以使用root调用:

Console.WriteLine("The size is {0} bytes.", DirSize(new DirectoryInfo(targetFolder));
Run Code Online (Sandbox Code Playgroud)

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

  • 使用EnumerateFiles和EnumerateDirectories (4认同)

Mik*_*son 24

我不相信有一个Win32 API来计算目录所占用的空间,虽然我有必要对此进行更正.如果有的话我会假设资源管理器会使用它.如果在资源管理器中获取大型目录的属性,则为您提供文件夹大小所需的时间与其包含的文件/子目录的数量成正比.

你的日常工作看起来相当简洁.请记住,您正在计算文件长度的总和,而不是磁盘上消耗的实际空间.群集,文件流等末尾的浪费空间所占用的空间将被忽略.

  • 此方法还会忽略联结,硬链接,压缩和脱机存储. (6认同)

Tri*_*shi 22

可以遵循最佳和最短的一种班轮方式

  long length = Directory.GetFiles(directoryPath,"*",SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
Run Code Online (Sandbox Code Playgroud)


Meh*_*dad 15

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

您的第一个问题是"文件大小" 至少有四个定义:

  • "文件结束"偏移量,即从文件的开头到结尾必须跳过的字节数.
    换句话说,它是文件中逻辑上的字节数(从使用角度来看).

  • "有效数据长度",等于实际未存储的第一个字节的偏移量.
    这始终小于或等于"文件结尾",并且是簇大小的倍数.
    例如,1 GB文件的有效数据长度为1 MB.如果您要求Windows读取前8 MB,它将读取前1 MB并假装其余数据存在,并将其作为零返回.

  • 文件的"已分配大小".这始终大于或等于"文件结尾".
    这是操作系统为文件分配的簇数乘以簇大小.
    与"文件结尾"大于"有效数据长度"的情况不同,多余的字节被视为文件数据的一部分,因此如果您尝试读取,操作系统将不会用零填充缓冲区超出文件末尾的已分配区域.

  • 文件的"压缩大小",仅对压缩(和稀疏?)文件有效.
    它等于群集的大小,乘以实际分配给此文件的卷上的群集数.
    对于非压缩和非稀疏文件,没有"压缩大小"的概念; 你会用"分配大小"代替.

你的第二个问题是像"文件"一样C:\Foo实际上可以有多个数据流.
此名称仅指默认流.一个文件可能有备用流,比如C:\Foo:Bar,其大小甚至没有在资源管理器中显示!

你的第三个问题是"文件"可以有多个名称("硬链接").
例如,C:\Windows\notepad.exe并且C:\Windows\System32\notepad.exe是两个名称相同文件. 任何名称都可用于打开文件的任何流.

你的第四个问题是"文件"(或目录)实际上甚至可能不是文件(或目录):
它可能是某个其他文件的软链接("符号链接"或"重新分析点")(或目录).
那个其他文件甚至可能不在同一个驱动器上.它甚至可能指向网络上的某些东西,或者它甚至可能是递归的!如果它是递归的,大小应该是无限的吗?

你的第五个是有"过滤器"驱动程序,使某些文件或目录看起来像实际文件或目录,即使它们不是.例如,微软的WIM映像文件(被压缩),可以使用一种称为ImageX工具文件夹中的"安装",而那些没有像重分析点或链接.它们看起来就像目录 - 除了它们实际上不是目录,而"大小"的概念对它们来说并没有意义.

您的第六个问题是每个文件都需要元数据.
例如,对同一文件具有10个名称需要更多元数据,这需要空间.如果文件名很短,那么拥有10个名称可能与拥有1个名称一样便宜 - 如果它们很长,那么拥有多个名称可以为元数据使用更多的磁盘空间.(同一个故事有多个流等等)
你也算这些吗?

  • 我糊涂了。这不是一个答案,而是一个(相当长的)问题,或者是多个问题。 (2认同)
  • 尽管它还不是答案,但它使我们在寻找答案方面迈出了一大步。 (2认同)

Gro*_*ozz 14

public static long DirSize(DirectoryInfo dir)
{
    return dir.GetFiles().Sum(fi => fi.Length) +
           dir.GetDirectories().Sum(di => DirSize(di));
}
Run Code Online (Sandbox Code Playgroud)

  • `PathTooLongException`(见[blogpost](http://blogs.msdn.com/b/bclteam/archive/2007/02/13/long-paths-in-net-part-1-of-3-kim- hamilton.aspx))和缺少凭证从某些子目录(`UnauthorizedAccessException`)浮现在脑海中.一个不太重要的问题应该是在运行时拔下可拆卸驱动器(USB Sticks等).这里的异常处理是必须的 - 只需在本地返回0并记录错误(如果求和结果应该是任何值).BTW:在远程共享上应用它可能看起来像DOS攻击.我敢肯定我至少错过了另一个案例:-) (2认同)
  • 我认为抛弃所有这些情况是可以接受的,因此默认行为很好.`StackOverflowException`是唯一应该处理的,虽然我不相信没有递归符号链接就能达到它. (2认同)

Kon*_* K. 6

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

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

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

  • 对于没有扩展名的文件,一如既往地使用“*”而不是“*.*” (2认同)

Ahm*_*bry 6

var size = new DirectoryInfo("E:\\").GetDirectorySize();
Run Code Online (Sandbox Code Playgroud)

这是Extension方法背后的代码

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.
}
Run Code Online (Sandbox Code Playgroud)

  • 在我的例子中,该代码的运行速度比任何使用 EnumerateFiles() 的答案都要快。 (2认同)

Ale*_*lex 5

更多更快!添加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());
          }
Run Code Online (Sandbox Code Playgroud)


Sco*_*ord 5

我使用相同的计数原理扩展了 @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; }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这个解决方案效果很好。它正在收集所有子文件夹:

Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
Run Code Online (Sandbox Code Playgroud)


Jam*_*zba 5

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

long sizeInBytes = Directory.EnumerateFiles("{path}","*", SearchOption.AllDirectories).Sum(fileInfo => new FileInfo(fileInfo).Length);
Run Code Online (Sandbox Code Playgroud)