UDF um Verzeichnisse schnell zu synchronisieren - Schnellere Robocopy Alternative

  • Hier ist mal ein Script, um Verzeichnisse zu synchronisieren. Ähnlich wie der Microsoft Befehl Robocopy Quelle Ziel /mir!
    Ihr könnt das Script gerne für Euch verwenden, aber bitte erwähnt meinen Namen im Quellcode, da ich sehr lange damit verbracht habe, die (in AutoIt) schnellste Möglichkeit zum synchronisieren zu finden.

    Vielleicht geht es noch ein kleines Stückchen schneller. Aber in meinen Test's war ich (mindestens bei grossen Verzeichnissen, bzw. vor allem über sehr langsame Netzwerke oder übers Internet) bis zu 4x schneller als Robocopy!

    UDF zum synchronisieren von Verzeichnissen
    [autoit]

    #cs
    ==================================================================================
    Function: _SyncDirectories($sSource, $sDestination, $bDeleteFiles = True)
    Description: Synchronize Source and Destination directory
    Parameter(s):
    $sSource Source-Path
    $sDestination Destination-Path
    $bDeleteFiles If True, then delete files and folders in destination which are not in the source
    Return: Success: 1
    Error: 0

    [/autoit] [autoit][/autoit] [autoit]

    Remark(s): Attention: Files which not exist in Source would be deleted in Destination!!
    Files which are newer in Destination would be overwrited with the "older" in Source!

    [/autoit] [autoit][/autoit] [autoit]

    Same sync as Robocopy.exe Source Destination /MIR
    BUT
    *********************
    Up to 4x faster (When Syncing large directories and syncing over slow network or slow Internet connection!)
    *********************
    Author(s): Veronesi
    Note(s):
    ==================================================================================
    #ce

    [/autoit] [autoit][/autoit] [autoit]

    #include-once

    [/autoit] [autoit][/autoit] [autoit]

    Func _SyncDirectories($sSource, $sDestination, $bDeleteFiles = True)
    ;This function searches recursively through all directories, but it don't use any recursion. It's iterativ!
    ;Iterativ is mostly faster then recursive. (Definitely on large directories!)
    ;AutoIt also has some limitations on recursive calls. (Max. 5100 levels; Probably we have sometimes more folder to sync....!)

    [/autoit] [autoit][/autoit] [autoit]

    ;Working with Scripting.Dictionary is much faster then _ArrayAdd
    ;And it's also MUCH faster then _ArraySearch for comparing directories
    ;It's also a bit faster then (_ArrayBinarySearch with _ArraySort) => _ArrayBinarySearch needs the _ArraySort!

    [/autoit] [autoit][/autoit] [autoit]

    Local Const $BinaryMode = 0 ;ASCII Mode => Uppercase and Lowercase are different! BinaryMode seams to be faster!
    Local Const $TextMode = 1 ;Uppercase and Lowercase are the same!
    Local Const $Database = 2 ;Only for Microsoft Access

    [/autoit] [autoit][/autoit] [autoit]

    Local $SyncSuccess = True, $SyncRet = 1, $TrimSourceForRelativePath, $TrimDestinationForRelativePath
    Local $oSourceFiles, $oSourceFolders, $oDestinationFiles, $oDestinationFolders, $oLocalFolders
    Local $Key, $Item, $aDFolders, $aSFolders, $iSF, $iDF, $bFound = False, $bOK = True
    Local $NumberOfFolders = 0, $iFolder = 0
    Local $hSearch, $sFile, $SearchPath

    [/autoit] [autoit][/autoit] [autoit]

    $oSourceFiles = ObjCreate('Scripting.Dictionary') ; Default = Binarymode
    $oSourceFolders = ObjCreate('Scripting.Dictionary') ; Default = Binarymode
    $oDestinationFiles = ObjCreate('Scripting.Dictionary') ; Default = Binarymode
    $oDestinationFolders = ObjCreate('Scripting.Dictionary') ; Default = Binarymode
    $oLocalFolders = ObjCreate('Scripting.Dictionary') ; Default = Binarymode
    If Not IsObj($oSourceFiles) Or Not IsObj($oSourceFolders) Or Not IsObj($oDestinationFiles) Or Not IsObj($oDestinationFolders) Or Not IsObj($oLocalFolders) Then Return SetError(1, 1, 0)

    [/autoit] [autoit][/autoit] [autoit]

    If StringRight($sSource, 1) = "\" Then $sSource = StringTrimRight($sSource, 1) ;Remove "\" at the end of the path (if found!)
    If StringRight($sDestination, 1) = "\" Then $sDestination = StringTrimRight($sDestination, 1) ;Remove "\" at the end of the path (if found!)

    [/autoit] [autoit][/autoit] [autoit]

    $TrimSourceForRelativePath = StringLen($sSource) + 1 ;Add 1 because the last Backslash is stripped!
    $TrimDestinationForRelativePath = StringLen($sDestination) + 1 ;Add 1 because the last Backslash is stripped!

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $oLocalFolders.Add($NumberOfFolders, $sSource) ;Add initial folder

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    If $oLocalFolders.Count = $iFolder Then ExitLoop
    $SearchPath = $oLocalFolders.Item($iFolder) ;Take next search path from the Dictionary object. Because we can't use the dictionary object with an Index, we use the Key as Index!
    $iFolder += 1

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $hSearch = FileFindFirstFile($SearchPath & "\*") ;Create Search handle in Path and search for all files / folders!
    If Not @error Then ;@error is set when folder is empty! Then it's no error!
    While 1
    $sFile = FileFindNextFile($hSearch) ;Search for next file
    If @error Then ExitLoop ;No further files found => Exit Loop
    If @extended Then ;Folder found!
    $NumberOfFolders += 1
    $oLocalFolders.Add($NumberOfFolders, $SearchPath & "\" & $sFile) ;Add folder for local use. Folder with full path!/ Use a ongoing number as key! Because the dictionary object can't be used with an index!
    $oSourceFolders.Add(StringTrimLeft($SearchPath & "\" & $sFile, $TrimSourceForRelativePath), 1) ;Add folder for global use. Take only the relative path!
    Else
    $oSourceFiles.Add(StringTrimLeft($SearchPath & "\" & $sFile, $TrimSourceForRelativePath), FileGetTime($SearchPath & "\" & $sFile, 0, 1)) ;Add file, but only relative path
    EndIf
    WEnd
    EndIf
    FileClose($hSearch) ;Close search handle

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    If $SearchPath = $sSource Then
    $SearchPath = $sDestination
    Else
    $SearchPath = $sDestination & "\" & StringTrimLeft($SearchPath, $TrimSourceForRelativePath)
    EndIf
    $hSearch = FileFindFirstFile($SearchPath & "\*") ;Create Search handle in Path and search for all files / folders!
    If Not @error Then ;@error is set when folder is empty! Then it's no error!
    While 1
    $sFile = FileFindNextFile($hSearch) ;Search for next file
    If @error Then ExitLoop ;No further files found => Exit Loop
    If @extended Then ;Folder found!
    $oDestinationFolders.Add(StringTrimLeft($SearchPath & "\" & $sFile, $TrimDestinationForRelativePath), 1) ;Add folder for global use. Take only the relative path!
    Else
    $oDestinationFiles.Add(StringTrimLeft($SearchPath & "\" & $sFile, $TrimDestinationForRelativePath), FileGetTime($SearchPath & "\" & $sFile, 0, 1)) ;Add file, but only relative path
    EndIf
    WEnd
    EndIf
    FileClose($hSearch) ;Close search handle

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ;Sync and Copy
    For $Key In $oSourceFiles ;For each file in source files...
    If Not $oDestinationFiles.Exists($Key) Then ;Don't exist => Copy it
    $SyncRet = FileCopy($sSource & "\" & $Key, $sDestination & "\" & $Key, 9)
    If $SyncRet = 0 Then $SyncSuccess = False
    Else ;Exist => Check Date
    If $oSourceFiles.Item($Key) <> $oDestinationFiles.Item($Key) Then
    $SyncRet = FileCopy($sSource & "\" & $Key, $sDestination & "\" & $Key, 9)
    If $SyncRet = 0 Then $SyncSuccess = False
    EndIf
    EndIf
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;Check for folders to be created
    For $Key In $oSourceFolders ;For each folder in source folders...
    If Not $oDestinationFolders.Exists($Key) Then DirCreate($sDestination & "\" & $Key) ;Create folder if it don't exist. (Only empty folders. Not empty folder would already be created by Filecopy!!)
    Next

    [/autoit] [autoit][/autoit] [autoit]

    ;Check for files and folders to be deleted!
    If $bDeleteFiles Then
    For $Key In $oDestinationFiles ;For each file in destination files...
    If Not $oSourceFiles.Exists($Key) Then $SyncRet = FileDelete($sDestination & "\" & $Key) ;Delete file if not found in source
    If $SyncRet = 0 Then $SyncSuccess = False
    Next

    [/autoit] [autoit][/autoit] [autoit]

    For $Key In $oDestinationFolders ;For each folder in destination folders...
    If Not $oSourceFolders.Exists($Key) Then $SyncRet = DirRemove($sDestination & "\" & $Key, 1) ;Delete folder if not found in source
    If $SyncRet = 0 Then $SyncSuccess = False
    Next
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    $oSourceFiles.RemoveAll
    $oSourceFolders.RemoveAll
    $oDestinationFiles.RemoveAll
    $oDestinationFolders.RemoveAll
    WEnd
    $oLocalFolders = 0 ;Destroy object
    $oSourceFiles = 0 ;Destroy objects
    $oSourceFolders = 0
    $oDestinationFiles = 0
    $oDestinationFolders = 0

    [/autoit] [autoit][/autoit] [autoit]

    If $SyncSuccess Then Return 1 ;DirGetSize($sSource) / 1024 / 1024
    Return 0
    EndFunc ;==>_SyncDirectories

    [/autoit]

    Gruss Veronesi