Как получить размер каталога (файлы в каталоге) на С#?

Я хочу иметь возможность получить размер одного из локальных каталогов с помощью С#. Я пытаюсь избежать следующего (псевдоподобный код), хотя в худшем случае мне придется согласиться на это:

    int GetSize(Directory)
    {
        int Size = 0;

        foreach ( File in Directory )
        {
            FileInfo fInfo of File;
            Size += fInfo.Size;
        }

        foreach ( SubDirectory in Directory )
        {
            Size += GetSize(SubDirectory);
        }
        return Size;
    }

В принципе, есть ли доступная Walk(), чтобы я мог пройти через дерево каталогов? Который спасет рекурсию прохождения через каждый подкаталог.

Ответ 1

Если вы используете Directory.GetFiles, вы можете сделать рекурсивное seach (используя SearchOption.AllDirectories), но в любом случае это немного шелушится (особенно если у вас нет доступа к одному из подкаталогов) - и может включать огромный массив возвращается (предупреждение klaxon...).

Я был бы доволен рекурсивным подходом, если бы не смог показать (через профилирование) узкое место; и тогда я бы, вероятно, переключился на (одноуровневый) Directory.GetFiles, используя Queue<string> для эмуляции рекурсии.

Обратите внимание, что в .NET 4.0 представлены некоторые методы перечисления файлов/каталогов на основе перечислений, которые сохраняются на больших массивах.

Ответ 2

Очень сжатый способ получить размер папки в .net 4.0 ниже. Он по-прежнему страдает от ограничения прохождения всех файлов рекурсивно, но он не загружает потенциально огромный массив имен файлов, и он содержит только две строки кода.

    private static long GetDirectorySize(string folderPath)
    {
        DirectoryInfo di = new DirectoryInfo(folderPath);
        return di.EnumerateFiles("*.*", SearchOption.AllDirectories).Sum(fi => fi.Length);
    }

Ответ 3

Здесь мой подход .NET 4.0

public static long GetFileSizeSumFromDirectory(string searchDirectory)
{
 var files = Directory.EnumerateFiles(searchDirectory);

 // get the sizeof all files in the current directory
 var currentSize = (from file in files let fileInfo = new FileInfo(file) select fileInfo.Length).Sum();

 var directories = Directory.EnumerateDirectories(searchDirectory);

 // get the size of all files in all subdirectories
 var subDirSize = (from directory in directories select GetFileSizeSumFromDirectory(directory)).Sum();

 return currentSize + subDirSize;
}

Или даже приятнее:

// get IEnumerable from all files in the current dir and all sub dirs
var files = Directory.EnumerateFiles(searchDirectory,"*",SearchOption.AllDirectories);

// get the size of all files
long sum = (from file in files let fileInfo = new FileInfo(file) select fileInfo .Length).Sum();

Как сказал Габриэль, это не удастся, если у вас есть ограниченная директория в каталоге searchDirectory!

Ответ 4

Вы можете скрыть свою рекурсию за методом расширения (чтобы избежать проблем, отмеченных марком методом GetFiles()):

public static IEnumerable<FileInfo> Walk(this DirectoryInfo directory)
{
    foreach(FileInfo file in directory.GetFiles())
    {
        yield return file;
    }

    foreach(DirectoryInfo subDirectory in directory.GetDirectories()
    { 
        foreach(FileInfo file in subDirectory.Walk())
        {
            yield return file;
        }
    }
}

(Вероятно, вы захотите добавить к нему некоторую обработку исключений для защищенных папок и т.д.)

Тогда:

int totalSize = 0;

foreach(FileInfo file in directory.Walk())
{
    totalSize += file.Length;
}

В принципе один и тот же код, но, возможно, немного аккуратный...

Ответ 5

Во-первых, простите мой бедный английский, o) У меня возникла проблема, которая привела меня на эту страницу: перечислить файлы каталога и его подкаталогов, не блокируя UnauthorizedAccessException, и, как новый метод .Net 4 DirectoryInfo.Enumerate..., получите первый результат до конца весь запрос.

С помощью различных примеров, найденных здесь и там в Интернете, я, наконец, пишу этот метод:

public static IEnumerable<FileInfo> EnumerateFiles_Recursive(this DirectoryInfo directory, string searchPattern, SearchOption searchOption, Func<DirectoryInfo, Exception, bool> handleExceptionAccess)
{
    Queue<DirectoryInfo> subDirectories = new Queue<DirectoryInfo>();
    IEnumerable<FileSystemInfo> entries = null;

    // Try to get an enumerator on fileSystemInfos of directory
    try
    {
        entries = directory.EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
    }
    catch (Exception e)
    {
        // If there a callback delegate and this delegate return true, we don't throw the exception
        if (handleExceptionAccess == null || !handleExceptionAccess(directory, e))
            throw;
        // If the exception wasn't throw, we make entries reference an empty collection
        entries = EmptyFileSystemInfos;
    }

    // Yield return file entries of the directory and enqueue the subdirectories
    foreach (FileSystemInfo entrie in entries)
    {
        if (entrie is FileInfo)
            yield return (FileInfo)entrie;
        else if (entrie is DirectoryInfo)
            subDirectories.Enqueue((DirectoryInfo)entrie);
    }

    // If recursive search, we make recursive call on the method to yield return entries of the subdirectories.
    if (searchOption == SearchOption.AllDirectories)
    {
        DirectoryInfo subDir = null;
        while (subDirectories.Count > 0)
        {
            subDir = subDirectories.Dequeue();
            foreach (FileInfo file in subDir.EnumerateFiles_Recursive(searchPattern, searchOption, handleExceptionAccess))
            {
                yield return file;
            }
        }
    }
    else
        subDirectories.Clear();
}

Я использую Очередь и рекурсивный метод для сохранения традиционного порядка (содержимого каталога, а затем содержимого первого подкаталога и его собственных подкаталогов, а затем содержимого второго...). Параметр "handleExceptionAccess" - это просто вызов функции, когда исключение создается с помощью каталога; функция должна возвращать true, чтобы указать, что исключение должно быть проигнорировано.

С помощью этого метода вы можете написать:

DirectoryInfo dir = new DirectoryInfo("c:\\temp");
long size = dir.EnumerateFiles_Recursive("*", SearchOption.AllDirectories, (d, ex) => true).Sum(f => f.Length);

И вот мы: все исключения при попытке перечислить каталог будут игнорироваться!

Надеемся на эту помощь

Лайонель

PS: по какой-то причине я не могу объяснить, мой метод быстрее, чем фреймворк 4...

PPS: вы можете получить мои тестовые решения с источником для этих методов: здесь TestDirEnumerate. Я пишу EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (используйте очередь, чтобы избежать рекурсии) и EnumerateFiles_NonRecursive_TraditionalOrder (используйте стек очереди, чтобы избежать рекурсии и сохранить традиционный порядок). Держите эти 3 метода без интереса, я пишу их только для теста лучшего. Я думаю, держать только последний. Я также написал эквивалент для EnumerateFileSystemInfos и EnumerateDirectories.

Ответ 7

Я искал некоторое время назад для функции, подобной той, которую вы запрашиваете, и из того, что я нашел в Интернете и на форумах MSDN, такой функции нет.

Рекурсивный способ - это единственное, что я нашел, чтобы получить размер папки, учитывая все файлы и подпапки, которые содержат.

Ответ 8

Вы должны облегчить себе. Сделайте способ и перейдите в расположение каталога.

    private static long GetDirectorySize(string location) {
        return new DirectoryInfo(location).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length);
    }

-G