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)
精彩评论