Как получить размер каталога (файлы в каталоге) в C #? - PullRequest
15 голосов
/ 13 июля 2009

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

    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 (), чтобы я мог пройти по дереву каталогов? Что сохранит рекурсию прохождения каждого подкаталога.

Ответы [ 9 ]

34 голосов
/ 10 мая 2011

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

private static long GetDirectorySize(string folderPath)
{
    DirectoryInfo di = new DirectoryInfo(folderPath);
    return di.EnumerateFiles("*.*", SearchOption.AllDirectories).Sum(fi => fi.Length);
}
19 голосов
/ 13 июля 2009

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

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

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

6 голосов
/ 10 февраля 2010

Вот мой .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 голосов
/ 13 июля 2009

Вы можете скрыть свою рекурсию за методом расширения (чтобы избежать проблем, выделенных Марком с помощью метода 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;
}

В основном тот же код, но, возможно, немного аккуратнее ...

3 голосов
/ 12 марта 2011

Во-первых, прости мой плохой английский; о) У меня возникла проблема, которая привела меня на эту страницу: перечислить файлы каталога и его подкаталогов, не блокируя исключение 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's 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);

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

Надеюсь, что эта помощь

Lionel

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

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

1 голос
/ 25 апреля 2018

решение уже здесь https://stackoverflow.com/a/12665904/1498669

как в дубликате Как получить размер папки в C #? показано -> вы можете сделать это также в c #

сначала добавьте ссылку COM "Microsoft Scripting Runtime" в ваш проект и используйте:

var fso = new Scripting.FileSystemObject();
var folder = fso.GetFolder(@"C:\Windows");
double sizeInBytes = folder.Size;
// cleanup COM
System.Runtime.InteropServices.Marshal.ReleaseComObject(folder);
System.Runtime.InteropServices.Marshal.ReleaseComObject(fso);

не забудьте очистить ссылки COM

1 голос
/ 13 июля 2009

Посмотрите на этот пост:

http://social.msdn.microsoft.com/forums/en-US/vbgeneral/thread/eed54ebe-facd-4305-b64b-9dbdc65df04e

В принципе, нет чистого способа .NET, но есть достаточно простой COM-подход, поэтому, если вы довольны использованием COM-взаимодействия и привязаны к Windows, это может сработать для вас.

0 голосов
/ 12 мая 2015

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

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

-G

0 голосов
/ 13 июля 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...