Как лучше всего рассчитать размер каталога в .NET? - PullRequest
64 голосов
/ 22 января 2009

Я написал следующую процедуру для ручного обхода каталога и вычисления его размера в 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?

Ответы [ 20 ]

52 голосов
/ 22 января 2009

Нет, это похоже на рекомендуемый способ для расчета размера каталога, соответствующий метод включен ниже:

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 - размер папки для расчета.

25 голосов
/ 22 января 2009

Я не верю, что существует Win32 API для расчета пространства, занимаемого каталогом, хотя я могу исправить это. Если бы это было так, я бы предположил, что Explorer будет использовать его. Если вы получаете Свойства большого каталога в Проводнике, время, необходимое для предоставления вам размера папки, пропорционально количеству файлов / подкаталогов, которые она содержит.

Ваша рутина кажется довольно аккуратной и простой. Помните, что вы вычисляете сумму длин файлов, а не фактическое пространство, занимаемое на диске. Пространство, занимаемое потерянным пространством в конце кластеров, файловых потоков и т. Д., Игнорируется.

19 голосов
/ 01 марта 2014

Самый лучший и самый короткий лайнер путь может быть следующим

  long length = Directory.GetFiles(directoryPath,"*",SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
15 голосов
/ 05 сентября 2013

Реальный вопрос в том, что вы собираетесь использовать для размера ?

Ваша первая проблема в том, что существует как минимум четыре определения для "размера файла":

  • Смещение «конец файла», которое представляет собой число байтов, которое необходимо пропустить, чтобы перейти от начала к концу файла.
    Другими словами, это число байтов логически в файле (с точки зрения использования).

  • «Действительная длина данных», равная смещению первого байта , который фактически не сохраняется .
    Это всегда меньше или равно «концу файла» и кратно размеру кластера.
    Например, файл размером 1 ГБ может иметь допустимую длину данных 1 МБ. Если вы попросите Windows прочитать первые 8 МБ, она прочитает первые 1 МБ и сделает вид, что оставшиеся данные были там, возвращая их в виде нулей.

  • «Выделенный размер» файла. Это всегда больше или равно «концу файла».
    Это количество кластеров, выделенных ОС для файла, умноженное на размер кластера.
    В отличие от случая, когда «конец файла» больше, чем «допустимая длина данных», избыточные байты не считаются частью данных файла, поэтому ОС не заполнить буфер нулями, если вы попытаетесь прочитать в выделенной области за концом файла.

  • «Сжатый размер» файла, который действителен только для сжатых (и разреженных?) Файлов.
    Он равен размеру кластера, умноженному на количество кластеров на томе, которые фактически выделены для этого файла.
    Для несжатых и не разреженных файлов не существует понятия «сжатый размер»; вместо этого вы бы использовали «выделенный размер».

Ваша вторая проблема заключается в том, что "файл", такой как C:\Foo, может иметь несколько потоков данных.
Это имя относится только к потоку по умолчанию . Файл может иметь альтернативных потоков, например C:\Foo:Bar, размер которых даже не отображается в Проводнике!

Ваша третья проблема в том, что "файл" может иметь несколько имен ("жесткие ссылки").
Например, C:\Windows\notepad.exe и C:\Windows\System32\notepad.exe - это два имени для одного и того же файла. Любое имя может использоваться для открытия любого потока файла.

Ваша четвертая проблема заключается в том, что "файл" (или каталог) на самом деле может даже не быть файлом (или каталогом):
Это может быть мягкая ссылка («символическая ссылка» или «точка повторной обработки») на другой файл (или каталог).
Этот другой файл может даже не находиться на том же диске. Это может даже указывать на что-то в сети, или это может быть даже рекурсивно! Должен ли размер быть бесконечностью, если он рекурсивный?

Ваш пятый состоит в том, что существуют драйверы "фильтра", которые делают определенные файлы или каталоги похожими на настоящие файлы или каталоги, даже если это не так. Например, файлы изображений WIM от Microsoft (которые сжимаются) можно «смонтировать» в папке с помощью инструмента ImageX, и эти не выглядят как точки повторного анализа или ссылки. Они выглядят так же, как каталоги - за исключением того, что вы на самом деле не каталоги, и понятие «размер» для них не имеет смысла.

Ваша шестая проблема в том, что для каждого файла требуются метаданные.
Например, наличие 10 имен для одного и того же файла требует больше метаданных, что требует места. Если имена файлов короткие, 10 имен могут быть такими же дешевыми, как и 1 имя, а если они длинные, то наличие нескольких имен может использовать больше дискового пространства для метаданных . (Та же история с несколькими потоками и т. Д.)
Вы тоже это считаете?

13 голосов
/ 24 мая 2012
public static long DirSize(DirectoryInfo dir)
{
    return dir.GetFiles().Sum(fi => fi.Length) +
           dir.GetDirectories().Sum(di => DirSize(di));
}
5 голосов
/ 28 августа 2013

Еще быстрее! Добавить COM-ссылку "Windows Script Host Object ..."

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());
          }
5 голосов
/ 03 сентября 2015
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.
}
4 голосов
/ 22 января 2015

Я расширил ответ @ Хао, используя тот же принцип подсчета, но поддерживая более богатый возврат данных, так что вы получите обратно размер, рекурсивный размер, количество каталогов и количество рекурсивных каталогов, 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; }
    }
}
4 голосов
/ 13 июня 2009

До недавнего времени я возился с 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()

Это коротко, он ищет подкаталоги и прост для понимания, если вы знаете синтаксис LINQ. Вы можете даже указать символы подстановки для поиска определенных файлов, используя третий параметр функции .GetFiles.

Я не эксперт по C #, но вы можете добавить пространство имен My на C # таким образом .

Я думаю, что этот способ получения размера папки не только короче и более современен, чем способ, описанный в ссылке Хао , он в основном использует тот же метод loop-of-FileInfo, описанный в конце.

4 голосов
/ 22 августа 2016

это решение работает очень хорошо. он собирает все подпапки:

Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...