program story

.NET에서 디렉토리 크기를 계산하는 가장 좋은 방법은 무엇입니까?

inputbox 2020. 11. 13. 08:09
반응형

.NET에서 디렉토리 크기를 계산하는 가장 좋은 방법은 무엇입니까?


디렉터리를 수동으로 탐색하고 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에서 폴더 크기를 계산하는 가장 효율적인 방법은 무엇입니까?


나는 이것에 대해 고칠 수 있지만 디렉토리가 소비하는 공간을 계산하는 Win32 API가 있다고 생각하지 않습니다. 그렇다면 Explorer가 그것을 사용할 것이라고 가정합니다. 탐색기에서 큰 디렉터리의 속성을 얻는 경우 폴더 크기를 제공하는 데 걸리는 시간은 포함 된 파일 / 하위 디렉터리의 수에 비례합니다.

당신의 일상은 상당히 깔끔하고 단순 해 보입니다. 디스크에서 사용되는 실제 공간이 아니라 파일 길이의 합계를 계산한다는 점에 유의하십시오. 클러스터, 파일 스트림 등의 끝에서 낭비 된 공간이 사용하는 공간은 무시됩니다.


아니요, 이는 아래에 포함 된 관련 방법 인 디렉터리 크기를 계산 하는 권장 방법같습니다.

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;  
}

루트를 다음과 같이 호출합니다.

Console.WriteLine("The size is {0} bytes.", DirSize(new DirectoryInfo(targetFolder));

... targetFolder계산할 폴더 크기는 어디 입니까?


가장 좋고 가장 짧은 한 라이너 방법은 다음과 같습니다.

  long length = Directory.GetFiles(directoryPath,"*",SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));

진짜 질문은 크기를 무엇에 사용할 계획 입니까?

귀하의 첫 번째 문제가 있다는 것입니다 적어도 "파일 크기"에 대한 정의 :

  • 파일의 처음부터 끝까지 이동하기 위해 건너 뛰어야하는 바이트 수인 "파일 끝"오프셋입니다.
    즉, 파일의 논리적 바이트 수입니다 (사용 측면에서).

  • 실제로 저장되지 않은 첫 번째 바이트의 오프셋과 동일한 "유효한 데이터 길이" 입니다.
    이것은 항상 "파일 끝"보다 작거나 같으며 클러스터 크기의 배수입니다.
    예를 들어, 1GB 파일은 1MB의 유효한 데이터 길이를 가질 수 있습니다. Windows에 처음 8MB를 읽도록 요청하면 처음 1MB를 읽고 나머지 데이터가있는 것으로 간주하여 0으로 반환합니다.

  • 파일의 "할당 된 크기". 이것은 항상 "파일 끝"보다 크거나 같습니다.
    OS가 파일에 할당 한 클러스터 수에 클러스터 크기를 곱한 값입니다.
    "파일의 끝"은 "유효한 데이터 길이"보다 큰 경우와는 달리, 초과 바이트되어 있지 OS가 있도록, 파일의 데이터의 일부로 간주 하지 당신이 읽으려고하면 제로와 버퍼를 채우기 파일의 끝을 넘어서 할당 된 영역.

  • 압축 된 (및 스파 스?) 파일에만 유효한 파일의 "압축 된 크기"입니다. 이 파일에 실제로 할당 된
    볼륨의 클러스터 수를 곱한 클러스터 크기와 같습니다. 비 압축 및 비 희소 파일의 경우 "압축 된 크기"라는 개념이 없습니다. 대신 "할당 된 크기"를 사용합니다.

당신의 두 번째 문제는 같은 "파일"는 것입니다 C:\Foo실제로 여러 가질 수 스트림 데이터를.
이 이름은 기본 스트림을 나타냅니다 . 파일에는 과 같은 대체 스트림 이있을 수 있으며 C:\Foo:Bar그 크기는 탐색기에도 표시되지 않습니다!

귀하의 세 번째 문제는 "파일이"할 수 있다는 것입니다 여러 이름이 ( "하드 링크를").
예를 들어, C:\Windows\notepad.exe그리고 C:\Windows\System32\notepad.exe두 가지 이름 에 대한 동일한 파일. 어떤 이름을 여는 데 사용 될 수 있는 파일의 스트림을.

번째 문제는 "파일"(또는 디렉토리)이 실제로 파일 (또는 디렉토리)
이 아닐 수도 있다는 것입니다. 다른 파일 ( "기호 링크"또는 "재분석 지점")에 대한 소프트 링크 일 수 있습니다. 또는 디렉토리).
다른 파일은 같은 드라이브에 있지 않을 수도 있습니다. 네트워크상의 무언가를 가리킬 수도 있고 재귀적일 수도 있습니다! 재귀적인 경우 크기는 무한대 여야합니까?

귀하의 다섯 번째는 특정 파일을 만들거나 디렉토리가 "필터"드라이버 있다는 것입니다 보면 그렇지 않은 경우에도 실제 파일이나 디렉토리와 같은가. 예를 들어, Microsoft의 WIM 이미지 파일 (압축 된)은 ImageX라는 도구를 사용하여 폴더에 "마운트"할 수 있으며 재분석 지점이나 링크처럼 보이지 않습니다 . 그것들은 실제로 디렉토리가 아니라는 점을 제외하고는 디렉토리와 똑같습니다. 그리고 "크기"라는 개념은 그들에게 실제로 의미가 없습니다.

귀하의 여섯 번째 문제는 모든 파일에 메타 데이터를 필요로한다는 것이다.
예를 들어 동일한 파일에 대해 이름이 10 개 있으면 메타 데이터가 더 많이 필요하므로 공간이 필요합니다. 파일 이름이 짧으면 10 개의 이름을 갖는 것이 1 개의 이름을 갖는 것만 큼 저렴할 수 있으며, 길이가 길면 이름이 여러 개인 경우 메타 데이터에 더 많은 디스크 공간 사용할 수 있습니다 . (여러 스트림이있는 같은 이야기 등)
이것도 계산합니까?


public static long DirSize(DirectoryInfo dir)
{
    return dir.GetFiles().Sum(fi => fi.Length) +
           dir.GetDirectories().Sum(di => DirSize(di));
}

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.
}

더 빠르게! 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());
          }

나는 최근까지 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 #에 My 네임 스페이스를 추가 할 수 있습니다 .

폴더 크기를 얻는이 방법은 Hao의 link 에 설명 된 방법보다 짧고 현대적 일뿐만 아니라 기본적으로 끝에 설명 된 동일한 loop-of-FileInfo 메서드를 사용한다고 생각합니다.


이것은 디렉토리의 크기를 계산하는 가장 좋은 방법입니다. 다른 방법 만이 여전히 재귀를 사용하지만 사용하기가 좀 더 쉽고 유연하지 않습니다.

float folderSize = 0.0f;
FileInfo[] files = Directory.GetFiles(folder, "*", SearchOption.AllDirectories);
foreach(FileInfo file in files) folderSize += file.Length;

동일한 계산 원칙을 사용하여 @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; }
    }
}

다음 방법은 재귀 함수보다 더 빠르게 작업을 수행하는 것으로 보입니다.

long size = 0;
DirectoryInfo dir = new DirectoryInfo(folder);
foreach (FileInfo fi in dir.GetFiles("*.*", SearchOption.AllDirectories))
{
   size += fi.Length;
}

간단한 콘솔 애플리케이션 테스트는이 루프가 재귀 함수보다 파일을 더 빠르게 합산하고 동일한 결과를 제공함을 보여줍니다. 이 코드를 단축하기 위해 Sum ()과 같은 LINQ 메서드를 사용하고 싶을 것입니다.


이 솔루션은 매우 잘 작동합니다. 모든 하위 폴더를 수집합니다.

Directory.GetFiles(@"MainFolderPath", "*", SearchOption.AllDirectories).Sum(t => (new FileInfo(t).Length));

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;
    }
}

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();

GetFileswith를 호출 하면 지정된 SearchOption.AllDirectories모든 subdirectories디렉토리에 있는 모든 파일의 전체 이름이 반환 됩니다. OS는 파일 크기를 바이트 단위로 나타냅니다. Length 속성에서 파일 크기를 검색 할 수 있습니다. 1024를 2의 거듭 제곱으로 나누면 파일 크기 (MB)가됩니다. 디렉토리 / 폴더에는 많은 파일이 포함될 수 있으므로 d.Select(x => x.FileSize)메가 바이트 단위로 측정 된 파일 크기 모음을 반환합니다. 마지막 호출 Sum()은 지정된 디렉토리에서 파일의 총 크기 찾습니다.

업데이트 : filterMask = " . "는 확장자가없는 파일에서는 작동하지 않습니다 .


최고의 알고리즘이되는 한 당신은 아마 그것을 올바르게 가지고있을 것입니다. 재귀 함수를 풀고 자신의 스택을 사용하는 것이 좋습니다 (스택 오버플로는 .Net 2.0+ 앱에서 세상의 끝이라는 것을 기억하십시오. 예외는 IIRC를 잡을 수 없습니다).

가장 중요한 것은 UI의 어떤 형태로든 사용하는 경우 업데이트로 UI 스레드에 신호를 보내는 작업자 스레드에 배치하는 것입니다.


성능을 향상시키기 위해 TPL (Task Parallel Library)을 사용할 수 있습니다. 다음은 좋은 샘플입니다. 디렉터리 파일 크기 계산-속도를 높이는 방법은 무엇입니까?

나는 그것을 테스트하지 않았지만 저자는 그것이 다중 스레드가 아닌 방법보다 3 배 빠르다고 말합니다.


The fastest way that I came up is using EnumerateFiles with SearchOption.AllDirectories. This method also allows updating the UI while going through the files and counting the size. Long path names don't cause any problems since FileInfo or DirectoryInfo are not tried to be created for the long path name. While enumerating files even though the filename is long the FileInfo returned by the EnumerateFiles don't cause problems as long as the starting directory name is not too long. There is still a problem with 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());
        }
    }

This .NET core command line app here calculates directory sizes for a given path:

https://github.com/garethrbrown/folder-size

The key method is this one which recursively inspects sub-directories to come up with a total 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;
    }
}

An alternative to Trikaldarshi's one line solution. (It avoids having to construct FileInfo objects)

long sizeInBytes = Directory.EnumerateFiles("{path}","*", SearchOption.AllDirectories).Sum(fileInfo => new FileInfo(fileInfo).Length);

I know this not a .net solution but here it comes anyways. Maybe it comes handy for people that have windows 10 and want a faster solution. For example if you run this command con your command prompt or by pressing winKey + R:

bash -c "du -sh /mnt/c/Users/; sleep 5"    

The sleep 5 is so you have time to see the results and the windows does not closes

In my computer that displays:

enter image description here

Note at the end how it shows 85G (85 Gigabytes). It is supper fast compared to doing it with .Net. If you want to see the size more accurately remove the h which stands for human readable.

So just do something like Processes.Start("bash",... arguments) That is not the exact code but you get the idea.

참고URL : https://stackoverflow.com/questions/468119/whats-the-best-way-to-calculate-the-size-of-a-directory-in-net

반응형