开发者

What’s the best way to calculate the size of a directory in VB .NET?

I need to calculate the directory size in VB .Net

I know the following 2 methods

Method 1: from MSDN http://msdn.microsoft.com/en-us/library/system.io.directory.aspx

' The following example calculates the size of a directory ' and its subdirectories, if any, and displays the total size ' in bytes.

Imports System
Imports System.IO

Public Class ShowDirSize

Public Shared Function DirSize(ByVal d As DirectoryInfo) As Long
    Dim Size As Long = 0
    ' Add file sizes.
    Dim fis As Fi开发者_Go百科leInfo() = d.GetFiles()
    Dim fi As FileInfo
    For Each fi In fis
        Size += fi.Length
    Next fi
    ' Add subdirectory sizes.
    Dim dis As DirectoryInfo() = d.GetDirectories()
    Dim di As DirectoryInfo
    For Each di In dis
        Size += DirSize(di)
    Next di
    Return Size
End Function 'DirSize

Public Shared Sub Main(ByVal args() As String)
    If args.Length <> 1 Then
        Console.WriteLine("You must provide a directory argument at the command line.")
    Else
        Dim d As New DirectoryInfo(args(0))
        Dim dsize As Long = DirSize(d)
        Console.WriteLine("The size of {0} and its subdirectories is {1} bytes.", d, dsize)
    End If
End Sub 'Main
End Class 'ShowDirSize

Method 2: from What's the best way to calculate the size of a directory in .NET?

Dim size As Int64 = (From strFile In My.Computer.FileSystem.GetFiles(strFolder, _
              FileIO.SearchOption.SearchAllSubDirectories) _
              Select New System.IO.FileInfo(strFile).Length).Sum()

Both these methods work fine. However they take lot of time to calculate the directory size if there are lot of sub-folders. e.g i have a directory with 150,000 sub-folders. The above methods took around 1 hr 30 mins to calculate the size of the directory. However, if I check the size from windows it takes less than a minute.

Please suggest better and faster ways to calculate the size of the directory.


Though this answer is talking about Python, the concept applies here as well.

Windows Explorer uses system API calls FindFirstFile and FindNextFile recursively to pull file information, and then can access the file sizes very quickly through the data that's passed back via a struct, WIN32_FIND_DATA: http://msdn.microsoft.com/en-us/library/aa365740(v=VS.85).aspx.

My suggestion would be to implement these API calls using P/Invoke, and I believe you will experience significant performance gains.


Doing the work in parallel should make it faster, at least on multi-core machines. Try this C# code. You will have to translate for VB.NET.

private static long DirSize(string sourceDir, bool recurse) 
{ 
    long size = 0; 
    string[] fileEntries = Directory.GetFiles(sourceDir); 

    foreach (string fileName in fileEntries) 
    { 
        Interlocked.Add(ref size, (new FileInfo(fileName)).Length); 
    } 

    if (recurse) 
    { 
        string[] subdirEntries = Directory.GetDirectories(sourceDir); 

        Parallel.For<long>(0, subdirEntries.Length, () => 0, (i, loop, subtotal) => 
        { 
            if ((File.GetAttributes(subdirEntries[i]) & FileAttributes.ReparsePoint) != FileAttributes.ReparsePoint) 
            { 
                subtotal += DirSize(subdirEntries[i], true); 
                return subtotal; 
            } 
            return 0; 
        }, 
            (x) => Interlocked.Add(ref size, x) 
        ); 
    } 
    return size; 
} 


This is a short and sweet code snippet that will get the job done. You just need to reset the counter before you call the function

Public Class Form1
Dim TotalSize As Long = 0
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    TotalSize = 0 'Reset the counter
    Dim TheSize As Long = GetDirSize("C:\Test")
    MsgBox(FormatNumber(TheSize, 0) & " Bytes" & vbCr & _
           FormatNumber(TheSize / 1024, 1) & " Kilobytes" & vbCr & _
           FormatNumber(TheSize / 1024 / 1024, 1) & " Megabytes" & vbCr & _
           FormatNumber(TheSize / 1024 / 1024 / 1024, 1) & " Gigabytes")
End Sub
Public Function GetDirSize(RootFolder As String) As Long
    Dim FolderInfo = New IO.DirectoryInfo(RootFolder)
    For Each File In FolderInfo.GetFiles : TotalSize += File.Length
    Next
    For Each SubFolderInfo In FolderInfo.GetDirectories : GetDirSize(SubFolderInfo.FullName)
    Next
    Return TotalSize
End Function
End Class


Big thanks @Jamie for Code and @Mathiasfk for translating to VB.net. I use it for my own Backup program which in default setting just backup the whole profile folder, it's a code which finally also is able to understand junction points and read more or less correct size. It's at least okay for Backup. :-)

I just had to put code within Try so it doesn't stop for folders it doesn't have access to, if you also could have such problem just use this (doesn't handle the error just skip it, you can add if important for you):

Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks

Public Function GetFolderSize(ByVal path As String, Optional recurse As Boolean = True) As Long
    Dim totalSize As Long = 0

    Try
        Dim files() As String = Directory.GetFiles(path)
        Parallel.For(0, files.Length,
               Sub(index As Integer)
                   Dim fi As New FileInfo(files(index))
                   Dim size As Long = fi.Length
                   Interlocked.Add(totalSize, size)
               End Sub)
    Catch ex As Exception
    End Try

    Try
        If recurse Then
            Dim subDirs() As String = Directory.GetDirectories(path)
            Dim subTotal As Long = 0
            Parallel.For(0, subDirs.Length,
                   Function(index As Integer)
                       If (File.GetAttributes(subDirs(index)) And FileAttributes.ReparsePoint) <> FileAttributes.ReparsePoint Then
                           Interlocked.Add(subTotal, GetFolderSize(subDirs(index), True))
                           Return subTotal
                       End If
                       Return 0
                   End Function)
            Interlocked.Add(totalSize, subTotal)
        End If
    Catch ex As Exception
    End Try

    Return totalSize
End Function


Here this as short as I can get it.

It will display the size of the selected in a message box. You're going to need a FolderBrowserDialog in the form for this to work.

Class Form1

Private Sub form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Try
        If (FolderBrowserDialog1.ShowDialog() = DialogResult.OK) Then
        Else : End
        End If
        Dim dInfo As New IO.DirectoryInfo(FolderBrowserDialog1.SelectedPath)
        Dim sizeOfDir As Long = DirectorySize(dInfo, True)
        MsgBox("Showing Directory size of " & FolderBrowserDialog1.SelectedPath _
               & vbNewLine & "Directory size in Bytes : " & "Bytes " & sizeOfDir _
               & vbNewLine & "Directory size in KB : " & "KB " & Math.Round(sizeOfDir / 1024, 3) _
               & vbNewLine & "Directory size in MB : " & "MB " & Math.Round(sizeOfDir / (1024 * 1024), 3) _
               & vbNewLine & "Directory size in GB : " & "GB " & Math.Round(sizeOfDir / (1024 * 1024 * 1024), 3))
    Catch ex As Exception
    End Try
End Sub

Private Function DirectorySize(ByVal dInfo As IO.DirectoryInfo, ByVal includeSubDir As Boolean) As Long
    Dim totalSize As Long = dInfo.EnumerateFiles().Sum(Function(file) file.Length)
    If includeSubDir Then totalSize += dInfo.EnumerateDirectories().Sum(Function(dir) DirectorySize(dir, True))
    Return totalSize
End Function

End Class


VB Code based on Jamie's answer:

Imports System.Threading
Imports System.IO

Public Function GetDirectorySize(ByVal path As String, Optional recurse As Boolean = False) As Long
    Dim totalSize As Long = 0
    Dim files() As String = Directory.GetFiles(path)
    Parallel.For(0, files.Length,
                   Sub(index As Integer)
                     Dim fi As New FileInfo(files(index))
                     Dim size As Long = fi.Length
                     Interlocked.Add(totalSize, size)
                   End Sub)

    If recurse Then
        Dim subDirs() As String = Directory.GetDirectories(path)
        Dim subTotal As Long = 0
        Parallel.For(0, subDirs.Length,
                       Function(index As Integer)
                         If (File.GetAttributes(subDirs(index)) And FileAttributes.ReparsePoint) <> FileAttributes.ReparsePoint Then
                           Interlocked.Add(subTotal, GetDirectorySize(subDirs(index), True))
                           Return subTotal
                         End If
                         Return 0
                       End Function)
      Interlocked.Add(totalSize, subTotal)
    End If

    Return totalSize
End Function


Try this to get total size in GB

    Dim fso = CreateObject("Scripting.FileSystemObject")
    Dim profile = fso.GetFolder("folder_path")
    MsgBox(profile.Size / 1073741824)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜