我需要计算数百个文件夹的大小,有些可能是10MB,而有些可能是10GB。我需要使用C#快速获取每个文件夹的大小。
我的最终结果希望是:
文件夹1 10.5GB
文件夹2 230MB
文件夹3 1.2GB
...
我需要计算数百个文件夹的大小,有些可能是10MB,而有些可能是10GB。我需要使用C#快速获取每个文件夹的大小。
我的最终结果希望是:
文件夹1 10.5GB
文件夹2 230MB
文件夹3 1.2GB
...
添加对 Microsoft Scripting Runtime 的引用,然后使用以下代码:
Scripting.FileSystemObject fso = new Scripting.FileSystemObject();
Scripting.Folder folder = fso.GetFolder([folder path]);
Int64 dirSize = (Int64)folder.Size;
如果您只需要大小,这比递归要快得多。
好的,这很糟糕,但是...
使用一个名为dirsize.bat的递归dos批处理文件:
@ECHO OFF
IF %1x==x GOTO start
IF %1x==DODIRx GOTO dodir
SET CURDIR=%1
FOR /F "usebackq delims=" %%A IN (`%0 DODIR`) DO SET ANSWER=%%A %CURDIR%
ECHO %ANSWER%
GOTO end
:start
FOR /D %%D IN (*.*) DO CALL %0 "%%D"
GOTO end
:dodir
DIR /S/-C %CURDIR% | FIND "File(s)"
GOTO end
:end
注意:第5行最后一个“%%A”后应该有一个制表符,而不是空格。
这就是你要查找的数据。它可以很快地处理成千上万个文件。实际上,它可以在不到2秒钟的时间内扫描完我的整个硬盘。
按照以下方式执行文件dirsize | sort /R /+25
,以便首先列出最大的目录。
祝好运。
private static IDictionary<string, long> folderSizes;
public static long GetDirectorySize(string dirName)
{
// use memoization to keep from doing unnecessary work
if (folderSizes.ContainsKey(dirName))
{
return folderSizes[dirName];
}
string[] a = Directory.GetFiles(dirName, "*.*");
long b = 0;
foreach (string name in a)
{
FileInfo info = new FileInfo(name);
b += info.Length;
}
// recurse on all the directories in current directory
foreach (string d in Directory.GetDirectories(dirName))
{
b += GetDirectorySize(d);
}
folderSizes[dirName] = b;
return b;
}
static void Main(string[] args)
{
folderSizes = new Dictionary<string, long>();
GetDirectorySize(@"c:\StartingFolder");
foreach (string key in folderSizes.Keys)
{
Console.WriteLine("dirName = " + key + " dirSize = " + folderSizes[key]);
}
// now folderSizes will contain a key for each directory (starting
// at c:\StartingFolder and including all subdirectories), and
// the dictionary value will be the folder size
}
如果你右键点击一个大目录,然后选择属性,你会发现计算大小需要相当长的时间... 我认为我们无法在这方面超越微软。你可以做的一件事是索引目录/子目录的大小,如果你要一遍又一遍地计算它们... 这将显著提高速度。
你可以使用类似于以下代码来递归计算C#中的目录大小:
static long DirSize(DirectoryInfo directory)
{
long size = 0;
FileInfo[] files = directory.GetFiles();
foreach (FileInfo file in files)
{
size += file.Length;
}
DirectoryInfo[] dirs = directory.GetDirectories();
foreach (DirectoryInfo dir in dirs)
{
size += DirSize(dir);
}
return size;
}
using System.IO;
using System.Threading;
using System.Threading.Tasks;
class FileCounter
{
private readonly int _clusterSize;
private long _filesCount;
private long _size;
private long _diskSize;
public void Count(string rootPath)
{
// Enumerate files (without real execution of course)
var filesEnumerated = new DirectoryInfo(rootPath)
.EnumerateFiles("*", SearchOption.AllDirectories);
// Do in parallel
Parallel.ForEach(filesEnumerated, GetFileSize);
}
/// <summary>
/// Get real file size and add to total
/// </summary>
/// <param name="fileInfo">File information</param>
private void GetFileSize(FileInfo fileInfo)
{
Interlocked.Increment(ref _filesCount);
Interlocked.Add(ref _size, fileInfo.Length);
}
}
var fcount = new FileCounter("F:\\temp");
fcount.Count();
这种方法在 .net 平台上是我能找到的最好的方法。另外,如果您需要计算簇大小和实际磁盘大小,则可以执行以下操作:
using System.Runtime.InteropServices;
private long WrapToClusterSize(long originalSize)
{
return ((originalSize + _clusterSize - 1) / _clusterSize) * _clusterSize;
}
private static int GetClusterSize(string rootPath)
{
int sectorsPerCluster = 0, bytesPerSector = 0, numFreeClusters = 0, totalNumClusters = 0;
if (!GetDiskFreeSpace(rootPath, ref sectorsPerCluster, ref bytesPerSector, ref numFreeClusters,
ref totalNumClusters))
{
// Satisfies rule CallGetLastErrorImmediatelyAfterPInvoke.
// see http://msdn.microsoft.com/en-us/library/ms182199(v=vs.80).aspx
var lastError = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Error code {0}", lastError));
}
return sectorsPerCluster * bytesPerSector;
}
[DllImport(Kernel32DllImport, SetLastError = true)]
private static extern bool GetDiskFreeSpace(
string rootPath,
ref int sectorsPerCluster,
ref int bytesPerSector,
ref int numFreeClusters,
ref int totalNumClusters);
当然,您需要在第一个代码段中重写GetFileSize()函数:
private long _diskSize;
private void GetFileSize(FileInfo fileInfo)
{
Interlocked.Increment(ref _filesCount);
Interlocked.Add(ref _size, fileInfo.Length);
Interlocked.Add(ref _diskSize, WrapToClusterSize(fileInfo.Length));
}
Dot Net Pearls有一个类似于这里描述的方法。令人惊讶的是System.IO.DirectoryInfo类没有一个方法来做到这一点,因为它似乎是一个常见的需求,而且可能会更快地完成它,而不需要在每个文件系统对象上进行本机/托管转换。我认为,如果速度是关键,可以编写一个非托管对象来执行此计算,然后从托管代码中每个目录调用它一次。
我非常确定这个程序会慢得要死,但我会这样写:
using System.IO;
long GetDirSize(string dir) {
return new DirectoryInfo(dir)
.GetFiles("", SearchOption.AllDirectories)
.Sum(p => p.Length);
}