Как лучше всего рассчитать размер каталога в VB .NET?

Мне нужно рассчитать размер каталога в VB .Net

Я знаю следующие 2 метода

Метод 1: из MSDN http://msdn.microsoft.com/en-us/library/system.io.directory.aspx

В следующем примере вычисляется размер каталога и его подкаталогов, если они есть, и отображается общий размер в байтах.

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 FileInfo() = 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

Метод 2: из Что такое лучший способ рассчитать размер каталога в .NET?

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

Оба эти метода работают нормально. Однако на вычисление размера каталога уходит много времени, если есть много подпапок. например, у меня есть каталог со 150 000 подпапок. Вышеупомянутые методы заняли около 1 часа 30 минут, чтобы рассчитать размер каталога. Однако, если я проверю размер в окнах, это займет меньше минуты.

Пожалуйста, предложите более эффективные и быстрые способы расчета размера каталога.


person Virendra    schedule 08.07.2010    source источник


Ответы (7)


Хотя в этом ответе говорится о Python, концепция применимо и здесь.

Проводник Windows рекурсивно использует вызовы системного API FindFirstFile и FindNextFile для получения информации о файлах, а затем может очень быстро получить доступ к размерам файлов с помощью данных, которые передаются обратно через struct, WIN32_FIND_DATA: http://msdn.microsoft.com/en-us/library/aa365740(v=VS.85).aspx.

Я предлагаю реализовать эти вызовы API с помощью P / Invoke, и я считаю, что вы ощутите значительный прирост производительности.

person rakuo15    schedule 08.07.2010

Параллельное выполнение работы должно ускорить ее, по крайней мере, на многоядерных машинах. Попробуйте этот код C #. Вам нужно будет перевести для 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; 
} 
person Jamie    schedule 09.07.2010

Это короткий и приятный фрагмент кода, который выполнит свою работу. Вам просто нужно сбросить счетчик перед вызовом функции

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
person jfjsdflkj    schedule 14.09.2015
comment
@ Magicprog.fr, хорошая петля. Подскажите, в чем тут хитрость со сбросом счетчика? Почему он по-прежнему возвращает правильный размер папки со всеми вложенными папками? Я просто не понимаю ... - person LuckyLuke82; 09.01.2017
comment
@ LuckyLuke82 Оператор ':' позволяет вам притвориться, что есть еще одна строка, не нажимая Enter, поэтому он добавляет размер каждого файла, а затем рекурсивно добавляет файлы из каждого подкаталога ... хотя они забыли добавить размер подкаталога -каталоги в общий размер :) - person seadoggie01; 31.08.2018

Большое спасибо @Jamie за код и @Mathiasfk за перевод на VB.net. Я использую его для своей собственной программы резервного копирования, которая по умолчанию просто создает резервную копию всей папки профиля, это код, который, наконец, также может понимать точки соединения и читать более или менее правильный размер. По крайней мере, для Бэкапа это нормально. :-)

Мне просто пришлось поместить код в Try, чтобы он не останавливался для папок, к которым у него нет доступа, если у вас также может быть такая проблема, просто используйте это (не обрабатывает ошибку, просто пропустите ее, вы можете добавить, если это важно для тебя):

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
person Marcy    schedule 29.08.2019

Вот это как можно короче.

Он отобразит размер выбранного в окне сообщения. Вам понадобится FolderBrowserDialog в форме, чтобы это работало.

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
person Gamer_sa22    schedule 29.03.2017

Код VB на основе ответа Джейми:

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
person mathiasfk    schedule 26.08.2016
comment
Не компилируется. Interlocked не определено. - person stigzler; 20.08.2017
comment
@stigzler определен в System.Threading. Проверьте обновленный ответ. - person mathiasfk; 20.08.2017

Попробуйте это, чтобы получить общий размер в ГБ

    Dim fso = CreateObject("Scripting.FileSystemObject")
    Dim profile = fso.GetFolder("folder_path")
    MsgBox(profile.Size / 1073741824)
person BR1COP    schedule 09.02.2018