我已编写以下例程来手动遍历目录并在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中计算文件夹大小的最有效方法是什么?
答案 0 :(得分:52)
不,这看起来像recommended way来计算目录大小,下面包含相关方法:
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;
}
您可以使用root调用:
Console.WriteLine("The size is {0} bytes.", DirSize(new DirectoryInfo(targetFolder));
...其中targetFolder
是要计算的文件夹大小。
答案 1 :(得分:25)
我不相信有一个Win32 API来计算目录所占用的空间,尽管我要对此进行更正。如果有的话我会假设资源管理器会使用它。如果在资源管理器中获得大型目录的属性,则为您提供文件夹大小所需的时间与其包含的文件/子目录的数量成正比。
你的日常工作看起来相当整洁简单。请记住,您正在计算文件长度的总和,而不是磁盘上消耗的实际空间。群集,文件流等末尾的浪费空间所占用的空间将被忽略。
答案 2 :(得分:19)
最好的和最短的一线方式可以跟随
long length = Directory.GetFiles(directoryPath,"*",SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
答案 3 :(得分:15)
您的第一个问题是“文件大小”至少四个 定义:
“文件结束”偏移量,即从文件的开头到结尾必须跳过的字节数。
换句话说,它是文件中逻辑的字节数(从使用角度来看)。
“有效数据长度”,它等于实际未存储的第一个字节的偏移。
这始终小于或等于“文件结尾”,并且是簇大小的倍数
例如,1 GB文件的有效数据长度为1 MB。如果您要求Windows读取前8 MB,它将读取前1 MB并假装其余数据存在,并将其作为零返回。
文件的“已分配大小”。这总是大于或等于“文件结束” 这是操作系统为文件分配的簇数,乘以簇大小 与“文件结尾”大于“有效数据长度”的情况不同,多余的字节不被认为是文件数据的一部分,因此操作系统将不<如果您尝试读取文件末尾之外的已分配区域,则使用零填充缓冲区。
文件的“压缩大小”,仅对压缩(和稀疏?)文件有效。
它等于群集的大小,乘以实际分配到该文件的卷上的群集数。
对于非压缩和非稀疏文件,没有“压缩大小”的概念;你会改用“分配大小”。
您的第二个问题是C:\Foo
之类的“文件”实际上可能有多个流数据。
此名称仅引用默认流。文件可能包含备用流,例如C:\Foo:Bar
,其大小甚至不会显示在资源管理器中!
您的第三个问题是“文件”有多个名称(“硬链接”)。
例如,C:\Windows\notepad.exe
和C:\Windows\System32\notepad.exe
是相同文件的两个名称。 任何名称都可用于打开文件的任何流。
您的第四个问题是“文件”(或目录)实际上可能甚至不是文件(或目录):
它可能是软链接(“符号链接”或“重新分析点”)到其他文件(或目录)。
那个其他文件甚至可能不在同一个驱动器上。它甚至可能指向网络上的某些东西,或者它甚至可能是递归的!如果它是递归的,那么大小应该是无限的吗?
您的第五个是有“过滤器”驱动程序,使某些文件或目录看起来像实际文件或目录,即使它们不是。例如,Microsoft的WIM图像文件(已压缩)可以使用名为ImageX的工具“挂载”在文件夹上,而那些不看起来像重新分析点或链接。它们看起来就像目录 - 除了它们实际上不是目录,而“size”的概念对它们来说并不合理。
您的第六个问题是每个文件都需要元数据 例如,对同一文件具有10个名称需要更多元数据,这需要空间。如果文件名很短,那么拥有10个名称可能与拥有1个名称一样便宜 - 如果它们很长,那么拥有多个名称可以使用更多的磁盘空间来获取元数据。 (同一故事有多个流等) 你也数这些吗?
答案 4 :(得分:13)
public static long DirSize(DirectoryInfo dir)
{
return dir.GetFiles().Sum(fi => fi.Length) +
dir.GetDirectories().Sum(di => DirSize(di));
}
答案 5 :(得分:5)
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.
}
答案 6 :(得分:5)
更快!添加COM引用“Windows脚本宿主对象...”
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());
}
答案 7 :(得分:4)
我使用相同的计数主体扩展了@ Hao的答案,但支持更丰富的数据返回,因此您可以返回大小,递归大小,目录计数和递归目录计数,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; }
}
}
答案 8 :(得分:4)
此解决方案效果很好。 它正在收集所有子文件夹:
Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));
答案 9 :(得分:4)
看来,以下方法比递归函数更快地执行任务:
long size = 0;
DirectoryInfo dir = new DirectoryInfo(folder);
foreach (FileInfo fi in dir.GetFiles("*.*", SearchOption.AllDirectories))
{
size += fi.Length;
}
一个简单的控制台应用程序测试表明,这个循环比递归函数更快地对文件求和,并提供相同的结果。您可能希望使用LINQ方法(如Sum())来缩短此代码。
答案 10 :(得分:4)
直到最近,我一直在调整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#专家,但您可以在C#this way上添加My命名空间。
我认为这种获取文件夹大小的方式不仅比在Hao link中描述的方式更短更现代,它基本上使用了最后描述的相同的FileInfo循环方法。
答案 11 :(得分:4)
这是计算目录大小的最佳方法。只有其他方式仍然会使用递归,但更容易使用,并不灵活。
float folderSize = 0.0f;
FileInfo[] files = Directory.GetFiles(folder, "*", SearchOption.AllDirectories);
foreach(FileInfo file in files) folderSize += file.Length;
答案 12 :(得分:3)
public static long GetDirSize(string path)
{
try
{
return Directory.EnumerateFiles(path).Sum(x => new FileInfo(x).Length)
+
Directory.EnumerateDirectories(path).Sum(x => GetDirSize(x));
}
catch
{
return 0L;
}
}
答案 13 :(得分:3)
Directory.GetFiles(@"C:\Users\AliBayat","*.*",SearchOption.AllDirectories)
.Select (d => new FileInfo(d))
.Select (d => new { Directory = d.DirectoryName,FileSize = d.Length} )
.ToLookup (d => d.Directory )
.Select (d => new { Directory = d.Key,TotalSizeInMB =Math.Round(d.Select (x =>x.FileSize)
.Sum () /Math.Pow(1024.0,2),2)})
.OrderByDescending (d => d.TotalSizeInMB).ToList();
用GetFiles
调用SearchOption.AllDirectories
返回指定目录的所有subdirectories
中所有文件的全名。 OS表示文件大小(以字节为单位)。您可以从文件的长度属性中检索文件的大小。用1024除以2的幂可以得到文件的大小(以兆字节为单位)。因为目录/文件夹可以包含许多文件,所以d.Select(x => x.FileSize)
返回以兆字节为单位的文件大小的集合。对Sum()
的最后一次调用将找到指定目录中文件的总大小。
答案 14 :(得分:2)
要提高性能,可以使用任务并行库(TPL)。 以下是一个很好的示例:Directory file size calculation - how to make it faster?
我没有测试它,但作者说它比非多线程方法快3倍......
答案 15 :(得分:2)
就最好的算法而言,你可能已经做对了。我建议您解开递归函数并使用自己的堆栈(记住堆栈溢出是.Net 2.0+应用程序中的世界末日,例外情况不能被捕获IIRC)。
最重要的是,如果您在任何形式的UI中使用它,请将其放在工作线程上,该线程向UI线程发出更新信号。
答案 16 :(得分:1)
我提出的最快的方法是使用带有SearchOption.AllDirectories的EnumerateFiles。此方法还允许在浏览文件和计算大小时更新UI。长路径名称不会导致任何问题,因为不会尝试为长路径名创建FileInfo或DirectoryInfo。在枚举文件时,即使文件名很长,只要起始目录名不太长,EnumerateFiles返回的FileInfo就不会引起问题。 UnauthorizedAccess仍然存在问题。
private void DirectoryCountEnumTest(string sourceDirName)
{
// Get the subdirectories for the specified directory.
long dataSize = 0;
long fileCount = 0;
string prevText = richTextBox1.Text;
if (Directory.Exists(sourceDirName))
{
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
foreach (FileInfo file in dir.EnumerateFiles("*", SearchOption.AllDirectories))
{
fileCount++;
try
{
dataSize += file.Length;
richTextBox1.Text = prevText + ("\nCounting size: " + dataSize.ToString());
}
catch (Exception e)
{
richTextBox1.AppendText("\n" + e.Message);
}
}
richTextBox1.AppendText("\n files:" + fileCount.ToString());
}
}
答案 17 :(得分:1)
多线程示例可根据Microsoft Docs计算目录大小,这会更快
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
long totalSize = 0;
String[] args = Environment.GetCommandLineArgs();
if (args.Length == 1) {
Console.WriteLine("There are no command line arguments.");
return;
}
if (! Directory.Exists(args[1])) {
Console.WriteLine("The directory does not exist.");
return;
}
String[] files = Directory.GetFiles(args[1]);
Parallel.For(0, files.Length,
index => { FileInfo fi = new FileInfo(files[index]);
long size = fi.Length;
Interlocked.Add(ref totalSize, size);
} );
Console.WriteLine("Directory '{0}':", args[1]);
Console.WriteLine("{0:N0} files, {1:N0} bytes", files.Length, totalSize);
}
}
// The example displaysoutput like the following:
// Directory 'c:\windows\':
// 32 files, 6,587,222 bytes
此示例仅计算当前文件夹中的文件,因此,如果要递归计算所有文件,则可以更改
String[] files = Directory.GetFiles(args[1]);
到
String[] files = Directory.GetFiles(args[1], "*", SearchOption.AllDirectories);
答案 18 :(得分:0)
此.NET Core命令行应用程序在此处计算给定路径的目录大小:
https://github.com/garethrbrown/folder-size
关键方法是这种方法,它递归检查子目录以得出总大小。
private static long DirectorySize(SortDirection sortDirection, DirectoryInfo directoryInfo, DirectoryData directoryData)
{
long directorySizeBytes = 0;
// Add file sizes for current directory
FileInfo[] fileInfos = directoryInfo.GetFiles();
foreach (FileInfo fileInfo in fileInfos)
{
directorySizeBytes += fileInfo.Length;
}
directoryData.Name = directoryInfo.Name;
directoryData.SizeBytes += directorySizeBytes;
// Recursively add subdirectory sizes
DirectoryInfo[] subDirectories = directoryInfo.GetDirectories();
foreach (DirectoryInfo di in subDirectories)
{
var subDirectoryData = new DirectoryData(sortDirection);
directoryData.DirectoryDatas.Add(subDirectoryData);
directorySizeBytes += DirectorySize(sortDirection, di, subDirectoryData);
}
directoryData.SizeBytes = directorySizeBytes;
return directorySizeBytes;
}
}
答案 19 :(得分:0)
我知道这不是.net解决方案,但是无论如何它都来了。也许对于拥有Windows 10并想要更快解决方案的人来说很方便。例如,如果您在命令提示符下运行此命令或通过按winKey + R
:
bash -c "du -sh /mnt/c/Users/; sleep 5"
sleep 5
是这样,因此您有时间查看结果并且窗口不会关闭
在我的计算机上显示:
最后请注意它如何显示85G(85 GB)。与使用.Net相比,它超级快速。如果您想更准确地查看尺寸,请删除h
(代表人类可读)。
所以只需执行类似Processes.Start("bash",... arguments)
的操作,那不是确切的代码,但是您可以理解。
答案 20 :(得分:0)
我尝试更改示例(Alexandre Pepin和hao的答案)
原样
private long GetDirectorySize(string dirPath)
{
if (Directory.Exists(dirPath) == false)
{
return 0;
}
DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
long size = 0;
// Add file sizes.
FileInfo[] fis = dirInfo.GetFiles();
foreach (FileInfo fi in fis)
{
size += fi.Length;
}
// Add subdirectory sizes.
DirectoryInfo[] dis = dirInfo.GetDirectories();
foreach (DirectoryInfo di in dis)
{
size += GetDirectorySize(di.FullName);
}
return size;
}
成为
private long GetDirectorySize2(string dirPath)
{
if (Directory.Exists(dirPath) == false)
{
return 0;
}
DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
long size = 0;
// Add file sizes.
IEnumerable<FileInfo> fis = dirInfo.EnumerateFiles("*.*", SearchOption.AllDirectories);
foreach (FileInfo fi in fis)
{
size += fi.Length;
}
return size;
}
最后您可以检查结果
// ---------------------------------------------
// size of directory
using System.IO;
string log1Path = @"D:\SampleDirPath1";
string log2Path = @"D:\SampleDirPath2";
string log1DirName = Path.GetDirectoryName(log1Path);
string log2DirName = Path.GetDirectoryName(log2Path);
long log1Size = GetDirectorySize(log1Path);
long log2Size = GetDirectorySize(log2Path);
long log1Size2 = GetDirectorySize2(log1Path);
long log2Size2 = GetDirectorySize2(log2Path);
Console.WriteLine($@"{log1DirName} Size: {SizeSuffix(log1Size)}, {SizeSuffix(log1Size2)}
{log2DirName} Size: {SizeSuffix(log2Size)}, {SizeSuffix(log2Size2)}");
这是SizeSuffix函数
private static readonly string[] SizeSuffixes =
{ "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
/// <summary>
/// Size Display
/// </summary>
/// <param name="value">bytes 數值</param>
/// <param name="decimalPlaces">小數位數</param>
/// <returns></returns>
public static string SizeSuffix(Int64 value, int decimalPlaces = 2)
{
if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
if (value < 0) { return "-" + SizeSuffix(-value); }
if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
// mag is 0 for bytes, 1 for KB, 2, for MB, etc.
int mag = (int)Math.Log(value, 1024);
// 1L << (mag * 10) == 2 ^ (10 * mag)
// [i.e. the number of bytes in the unit corresponding to mag]
decimal adjustedSize = (decimal)value / (1L << (mag * 10));
// make adjustment when the value is large enough that
// it would round up to 1000 or more
if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
{
mag += 1;
adjustedSize /= 1024;
}
return string.Format("{0:n" + decimalPlaces + "} {1}",
adjustedSize,
SizeSuffixes[mag]);
}
答案 21 :(得分:-1)
Trikaldarshi的单行解决方案的替代方案。 (避免了构造FileInfo对象)
long sizeInBytes = Directory.EnumerateFiles("{path}","*", SearchOption.AllDirectories).Sum(fileInfo => fileInfo.Length);