计算 Windows 文件夹大小的最快方法是什么?

发布于 2024-09-02 01:37:14 字数 175 浏览 3 评论 0原文

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

我的最终结果希望是:

Folder1 10.5GB、

Folder2 230MB、

Folder3 1.2GB

...

I need to calculate the size of hundreds of folders, some will be 10MB some maybe 10GB, I need a super fast way of getting the size of each folder using C#.

My end result will hopefully be:

Folder1 10.5GB

Folder2 230MB

Folder3 1.2GB

...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

∝单色的世界 2024-09-09 01:37:14

添加对 Microsoft 脚本运行时的引用并使用:

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

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

Add a reference to the Microsoft Scripting Runtime and use:

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

If you just need the size, this is much faster than recursing.

月下伊人醉 2024-09-09 01:37:14

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

使用名为 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 |对 /R /+25 进行排序以便查看首先列出的最大目录。

祝你好运。

OK, this is terrible, but...

Use a recursive dos batch file called dirsize.bat:

@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

Note: There should be a tab character after the final "%%A" on line 5, not spaces.

This is the data you're looking for. It will do thousands of files fairly quickly. In fact, it does my entire harddrive in less than 2 seconds.

Execute the file like this dirsize | sort /R /+25 in order to see the largest directory listed first.

Good luck.

空城仅有旧梦在 2024-09-09 01:37:14

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

There is no simple way to do this in .Net; you will have to loop through every file and subdir.
See the examples here to see how it's done.

浮云落日 2024-09-09 01:37:14

如果您右键单击一个大目录,然后单击属性,您会发现计算大小需要花费大量时间...我认为我们无法在这方面击败 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;
}

If you right click a large directory then properties you can see that it takes significant amount of time to calculate the size... I don't think we can beat MS in this. One thing you could do is index the sizes of directories/subdirs, if you are going to calculate them over and over again... that would significantly increase the speed.

You could use something like this to calculate directory size in C# recursively

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;
}
谁许谁一生繁华 2024-09-09 01:37:14

您可以执行类似的操作,但是在获取文件夹大小时没有 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
    }

You can do something like this, but there's no fast=true setting when it comes to getting folder sizes, you have to add up the file sizes.

    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
    }
梦萦几度 2024-09-09 01:37:14

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

Dot Net Pearls has a method similar to the ones described here. It's surprising that the System.IO.DirectoryInfo class doesn't have a method to do this since it seems like a common need and it probably would be faster to do it without doing a native/managed transition on each file system object. I do think that if speed is the key thing, writing a non-managed object to do this calculation and then call it once per directory from managed code.

咿呀咿呀哟 2024-09-09 01:37:14

我能找到的计算文件大小及其在磁盘上的计数的 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));
    }

The fastest approach on 4.0-4.5 framework which I could find to calculate files size and their count on disk was:

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();

This approach appeared for me as the best which I could find on .net platform. Btw if you need to calculate cluster size and real size on disk, you can do next:

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);

And of course you need to rewrite GetFileSize() in first code section:

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));
    }
慈悲佛祖 2024-09-09 01:37:14

此链接中有一些线索(虽然是用 Python 写的)来自遇到类似性能问题的人。您可以尝试调用 Win32 API 来查看性能改进了,但最终你会遇到同样的问题:一项任务只能很快完成,如果你必须多次执行该任务,就会花费很多时间。您能更详细地说明您这样做的目的吗?它可能会帮助人们想出一些启发式的方法或一些秘籍来帮助你。如果您经常进行此计算,您是否会缓存结果?

There are some leads in this link (though it's in Python) from a person running into similar performance issues. You can try calling down into Win32 API to see if performance improves, but at the end you're going to run into the same issue: a task can only be done so quickly and if you have to do the task a lot of times, it will take a lot of time. Can you give more detail on what you're doing this for? It might help folks come up with a heuristic or some cheats to help you. If you're doing this calculation a lot, are you caching the results?

得不到的就毁灭 2024-09-09 01:37:14

我不知道它有多适合您的用例,但假设您的计算机使用 NTFS 文件系统,绝对最快的方法可能是直接读取文件表,解析它,并以这种方式找到大小。

您可以首先阅读规范,例如 libfsntfs 提供的一个 这里,或者这个$FILE_NAME 属性提供文件大小。

I don't know how well it fits your use case, but assuming your machine is using the NTFS file system, the absolutely fastest way would probably be to read the file table directly, parse it, and find the size that way.

You could start by reading a spec, such as the one libfsntfs provides here, or perhaps this one. The $FILE_NAME attribute provides the file size.

我乃一代侩神 2024-09-09 01:37:14

我很确定这会慢得要命
但我会这样写:

using System.IO;

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

I'm quite sure that this will be slow as hell,
but I'd write it like this:

using System.IO;

long GetDirSize(string dir) {
   return new DirectoryInfo(dir)
      .GetFiles("", SearchOption.AllDirectories)
      .Sum(p => p.Length);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文