Как получить размер каталога (файлы в каталоге) на С#?
Я хочу иметь возможность получить размер одного из локальных каталогов с помощью С#. Я пытаюсь избежать следующего (псевдоподобный код), хотя в худшем случае мне придется согласиться на это:
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.
Ответ 6
Взгляните на это сообщение:
http://social.msdn.microsoft.com/forums/en-US/vbgeneral/thread/eed54ebe-facd-4305-b64b-9dbdc65df04e
В принципе нет чистого пути .NET, но есть довольно простой подход к COM, поэтому, если вы довольны использованием COM-взаимодействия и привязаны к Windows, это может сработать для вас.
Ответ 7
Я искал некоторое время назад для функции, подобной той, которую вы запрашиваете, и из того, что я нашел в Интернете и на форумах MSDN, такой функции нет.
Рекурсивный способ - это единственное, что я нашел, чтобы получить размер папки, учитывая все файлы и подпапки, которые содержат.
Ответ 8
Вы должны облегчить себе. Сделайте способ и перейдите в расположение каталога.
private static long GetDirectorySize(string location) {
return new DirectoryInfo(location).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length);
}
-G