Sign in to follow this  
Followers 0
blindwig

UDF: Recursive directory size, output to XML

2 posts in this topic

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][1]=path
;[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
                EndIf
            Else
                $aFolder[0][2] += FileGetSize($sPath & '\' & $sFilename)
            EndIf
            $sFilename = FileFindNextFile($hSearch)
        WEnd
    EndIf
    FileClose($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]
        Next
    EndIf
;trim final array, set extended to the total file size, return the array
    ReDim $aFolder[$aFolder[0][0] + 1][3]
    SetExtended($aFolder[0][2])
    Return $aFolder
EndFunc
#endregion

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

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)
    Else
        $sFile = $sPath
        $sPath = ''
    EndIf
;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)
    Else
        Return StringLeft($sPath, $iSize - 3 - $iFile) & '...'  & $sFile
    EndIf
EndFunc

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])
        Next
        FileWriteLine($hFile, $sPreFix & '</folder>')
    Else
        FileWriteLine($hFile, '" />')
    EndIf
EndFunc
#endregion

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)
        FileClose($hOutFile)
    EndIf
    _ProgressDelete()
EndFunc

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:

Share this post


Link to post
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
Sign in to follow this  
Followers 0