Jump to content

UDF: Recursive directory size, output to XML


Recommended Posts

There's plenty of times that I need to find the total size of a directory (and all subdirectories). And then it's nice to be able to go to each of those subdirectories and see what their total size is, etc.

So I wrote a function that uses nested arrays to handle the job. Here's the code:

;Returns a Nested array
;[0][0]=number of lines
;[0][2]=total size in bytes
;[x][0]=folder name
;[x][1]=nested array (same as this, starting at [x][0] folder)
;[x][2]=size (recursive) of folder
#region private
Func __ArrayNGetDirTree($sPath, $fMin, $fMax)
    Local $hSearch = FileFindFirstFile($sPath & '\*'), $aFolder[100][3]
    $aFolder[0][0] = 0
    $aFolder[0][1] = $sPath
    $aFolder[0][2] = 0
;Search for files and folders, record folders in array, sum up file sizes
    If $hSearch <> -1 Then
        $sFilename = FileFindNextFile($hSearch)
        While Not @error
            If _FileIsDir($sPath & '\' & $sFilename) Then
                If $sFileName <> '.' And $sFileName <> '..' Then
                    $aFolder[0][0] += 1
                    If $aFolder[0][0] >= UBound($aFolder, 1) Then ReDim $aFolder[$aFolder[0][0] * 2][3]
                    $aFolder[$aFolder[0][0]][0] = $sFileName
                $aFolder[0][2] += FileGetSize($sPath & '\' & $sFilename)
            $sFilename = FileFindNextFile($hSearch)
;loop through folder list, get recursive
    If $aFolder[0][0] Then
        Local $fSlice = $fMax / $aFolder[0][0], $fCur
        For $i = 1 To $aFolder[0][0]
            $fCur = $fMin + $i * $fSlice
            _ProgressUpdate($fCur, $aFolder[$i][0], _FileTruncPath($sPath,42))
            $aFolder[$i][1] = __ArrayNGetDirTree($sPath & '\' & $aFolder[$i][0], $fCur, $fSlice)
            $aFolder[$i][2] = @extended
            $aFolder[0][2] += $aFolder[$i][2]
;trim final array, set extended to the total file size, return the array
    ReDim $aFolder[$aFolder[0][0] + 1][3]
    Return $aFolder

Func _ArrayNGetDirTree($sPath, $sProgressTitle = @ScriptName)
    _ProgressCreate('Scanning Folders...','',$sProgressTitle)
    $aRet = __ArrayNGetDirTree($sPath, 0, 100)
    Return $aRet

It uses my Multi-level Progress Bars, so either get those or comment out the lines calling _Progress*(), or convert them over to the built-in progress bars.

It uses some supporting functions:

; Function Name:    _FileIsDir
; Description:    Returns true or false weather given file is a directory or not
; Parameter(s):  $Path
; Requirement(s):
; Return Value(s):  0 = not a directory or does not exist, 1 = file exists and is a directory
; Author(s):        Mike Ratzlaff <mike@ratzlaff.org>
; Revision:      20050623A
Func _FileIsDir($Path)
;This function checkes to see if $FileName exists and if it is a Directory
    If StringInStr(FileGetAttrib($Path), 'D') Then Return 1
    Return 0
EndFunc  ;==>_FileIsDir

;Truncates $sPath so that its length is <= $iSize
Func _FileTruncPath($sPath, $iSize)
;split file from path
    Local $iSplit = StringInStr($sPath, '\', 0, -1)
    If $iSplit Then
        $sFile = StringMid($sPath, $iSplit)
        $sPath = StringLeft($sPath, $iSplit - 1)
        $sFile = $sPath
        $sPath = ''
;truncate and return new path
    Local $iPath = StringLen($sPath), $iFile = StringLen($sFile)
    If $iPath + $iFile <= $iSize Then
        Return $sPath & $sFile
    ElseIf $iFile > $iSize - 3 Then
        Return '...' & StringRight($sFile, $iSize - 3)
        Return StringLeft($sPath, $iSize - 3 - $iFile) & '...'  & $sFile

Then the code to export to XML:

;Takes the return of _ArrayNGetTree and writes it to an XML file
#region Private
Func __ArrayNDirTreeToXML(ByRef $aTree, $sPath, $iLevel, $hFile, $fMin, $fMax)
    Local $sPrefix = _StringRepeat(@Tab, $iLevel), $i
    FileWrite($hFile, $sPreFix & '<folder name="' & $sPath & '" size="' & $aTree[0][2])
    If $aTree[0][0] > 0 Then
        Local $fSlice = $fMax / $aTree[0][0], $fCur
        FileWriteLine($hFile, '" >')
        For $i = 1 to $aTree[0][0]
            $fCur = $fMin + $i * $fSlice
            __ArrayNDirTreeToXML($aTree[$i][1], $aTree[$i][0], $iLevel + 1, $hFile, $fCur, $fSlice)
            _ProgressUpdate($fCur, $aTree[$i][0])
        FileWriteLine($hFile, $sPreFix & '</folder>')
        FileWriteLine($hFile, '" />')

Func _ArrayNDirTreeToXML(ByRef $aTree, $sFilename, $sHeader='')
    _ProgressCreate('Writing to XML...','',@ScriptName)
    Local $hOutFile = FileOpen($sFilename, 2)
    If $hOutFile <> -1 Then
        If $sHeader Then FileWrite($hOutFile, $sHeader)
        __ArrayNDirTreeToXml($aTree, $aTree[0][1], 0, $hOutFile, 0, 100)

and a sample of how to use it:

$anMyDocs = _ArrayNGetDirTree(@MyDocumentsDir)
_ArrayNDirTreeToXML($anMyDocs, 'MyDocs.xml')
Run(@ComSpec & ' /c start DirSizes.xml')

If you use IE to view XML, it will pop up and give you a security warning. It's kind of stupid, because it's warning you about IE's own code to view XML. Click yes and everything will be fine, or if you're paranoid click no and you'll still be able to view the xml, just won't be able to expand/collapse the branches.

Anyway, I think that this routine is very useful for anyone who is trying to figure out where all their HD space keeps going.... :whistle:

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    • No registered users viewing this page.
  • Create New...