Jump to content

FileCopyWithProgress


NiVZ
 Share

Recommended Posts

Hello,

Wrote this little function to copy a file and show a progress bar as it copies.

Any suggestions for improvement welcome.

I've seen a few people ask for this so hope it helps.

** Edit: 06/11/2008 - If file can't be opened it now shows filename **

** Edit: 06/11/2008 - Progressbar Subtext does not wrap so changed to show source and destination on seperate lines

; Need to use a large file >2MB or add a Sleep(1000) to the For Loop
$FileCopy = _CopyWithProgress("C:\Test.txt", "C:\TEMP\Test2.txt", 2048)

; Check return code to see if file copied correctly
If $FileCopy = 0 Then
    MsgBox(0,"Copy Complete", "File Copy Successful.")
Else
    MsgBox(16,"Copy Complete", "File Copy Failed.")
EndIf


Func _CopyWithProgress($inSource, $inDest, $ChunkSize = 2048)
    
; Function to copy a file showing a realtime progress bar
;
; By Paul Niven (NiVZ)
;
; 05/11/2008
;
; Parameters:
;
; $inSource  -   Full path to source file
; $inDest   -    Full path to destination file (structure will be created if it does not exist
; $ChunkSize -   (Optional) Size of chunks to copy - setting this higher makes large files copy faster
;               Default chunk size is 2048
;
; Returns 0 if copy is successful and 1 if it fails
    
; Open source file for BINARY reading
    $SourceFile = FileOpen($inSource, 16)
    
; Check that file open was successful
    If $SourceFile = -1 Then
        MsgBox(16, "Error", "Unable to open source file: " & $inSource)
        Exit
    EndIf
    
; Open destination file for BINARY writing , overwriting existing file and creating directory structure if it doesn't exist
    $DestFile = FileOpen($inDest, 26)
    
; Check that file open was successful
    If $SourceFile = -1 Then
        MsgBox(16, "Error", "Unable to open destination file: " & $inDest)
        Exit
    EndIf
    
; Get the size of the file we are going to copy
    $SourceSize = FileGetSize($inSource)

; Calculate how many chunks we need to copy
    $Chunks = $SourceSize / $ChunkSize
    
; Show the progress bar
    ProgressOn("File Copy", "Copying...", "Source: " & $inSource & @LF & "Dest:  " & $inDest)
    
; Loop from 0 to number of chunks
    For $i = 0 to $Chunks
    
; Read the next chunk
        $Data = FileRead($SourceFile, $ChunkSize)
        
; Write the next chunk
        FileWrite($DestFile, $Data)
        
; Update the progress bar
        ProgressSet( (($i + 1) / $Chunks) * 100 )
    Next
    
; Hide the progress bar
    ProgressOff()
    
; Close the source file
    FileClose($SourceFile)
    
; Close the destination file
    FileClose($DestFile)
    
; Check the file copy was successful
    $DestSize = FileGetSize($inDest)
    
    If $SourceSize = $DestSize Then
        Return 0
    Else
        Return 1
    EndIf
EndFunc
Edited by NiVZ
Link to comment
Share on other sites

Found another better way to do this on the german AutoIt forum, so I've modified my script again to use their method. It uses Kernel32.dll and a DllCallback so it's the Windows filesystem thats performing the copy. This also makes it copy faster.

Credit to the guys on the german AutoIt site (hope they don't mind me modifying their code): http://www.autoit.de/index.php?page=Thread&postID=58875

NiVZ

; Need to use a large file >2MB
$FileCopy = _CopyWithProgress("C:\test.txt", "C:\TEMP\Test.txt")

; Check return code to see if file copied correctly
If $FileCopy = 0 Then
    MsgBox(0,"Copy Complete", "File Copy Successful.")
Else
    MsgBox(16,"Copy Complete", "File Copy Failed.")
EndIf


Func _CopyWithProgress($inSource, $inDest)
    
; Function to copy a file showing a realtime progress bar
;
; By Paul Niven (NiVZ)
;
; 05/11/2008
;
; Parameters:
;
; $inSource  -   Full path to source file
; $inDest   -    Full path to destination file (structure will be created if it does not exist
;
; Returns 0 if copy is successful and 1 if it fails
;
;
; Updates:
;
; 06/11/2008 - Now shows SOURCE and DEST on seperate lines while copying
;           - Now uses Kernal32 and DllCallBackRegister to provide faster copying
;             Kernal32 and DllCallBackRegister taken from _MultiFileCopy.au3
;             See: http://www.autoit.de/index.php?page=Thread&postID=58875
      

; Create dll hooks for Progress Update
    If Not IsDeclared("callback") Then Local $callback = DllCallbackRegister('__Progress', 'int', 'uint64;uint64;uint64;uint64;dword;dword;ptr;ptr;str')
    If Not IsDeclared("ptr") Then Local $ptr = DllCallbackGetPtr($callback)

; Check that file open was successful
    If Not FileExists($inSource) Then
        MsgBox(16, "Error", "Unable to open source file: " & $inSource)
        Exit
    EndIf
    
; Get Destination Directory - without filename
    $DestDir = StringLeft($inDest, StringInStr($inDest, "\", "", -1))

; Check destination directory exists and create it if it doesn't try to create it
    If Not FileExists($DestDir) Then
        $DirCreate = DirCreate($DestDir)
        If $DirCreate = 0 Then MsgBox(16, "Error", "Could not create destination directory: " & $DestDir)
    EndIf
    
; Show the progress bar
    ProgressOn("File Copy", "Copying...", "Source: " & $inSource & @LF & "Dest:  " & $inDest)
    
    $ret = DllCall('kernel32.dll', 'int', 'CopyFileExA', 'str', $inSource, 'str', $inDest, 'ptr', $ptr, 'str', '', 'int', 0, 'int', 0)

; Hide the progress bar
    ProgressOff()
    
    If $ret[0] <> 0 Then
    ; Success
        Return 0
    Else
    ; Fail
        Return 1
    EndIf
    
EndFunc


Func __Progress($FileSize, $BytesTransferred, $StreamSize, $StreamBytesTransferred, $dwStreamNumber, $dwCallbackReason, $hSourceFile, $hDestinationFile, $lpData)
    
    ProgressSet(Round($BytesTransferred / $FileSize * 100, 0))
    
EndFunc
Link to comment
Share on other sites

This is exactly, what the function in the German forum is designed for :mellow:http://www.autoit.de/index.php?page=Thread&postID=58875

Waht you want, is Example 1 ("Beispiel 1") :(

or this one :)http://www.autoitscript.com/forum/index.php?showtopic=11888

Edited by ProgAndy

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Link to comment
Share on other sites

  • 9 months later...

I wrote this script for the same reasons as above, I needed a progress bar while copying a single file, but reading/writing using a custom function wasn't yielding good performance. I found a way to use the copy command and retrieve information about how much data was being copied by using ProcessGetStats:

AutoItSetOption("MustDeclareVars", 1)
; Displays progress bar when copying a single file

If ($CmdLine[0] < 2) Then
    MsgBox(0, "Error", "You must specify both source and destination files")
    Exit
EndIf

Global $helpmsg
$helpmsg = "CopyFileProgress.exe [options] source destination" & @CRLF _
              & "  options:" & @CRLF _
              & "   /y = suppress prompt to overwrite destination file" & @CRLF _
              & "  /title:<title> = set title of copy dialog box" & @CRLF _
              & "  source = source file" & @CRLF _
              & "  destination = destination file" & @CRLF
Dim $forceoverwrite = 0
Dim $title = "Copying files..."
Dim $source = ""
Dim $destination = ""
Dim $cmdopts = $CmdLine

; Loop through command args for switches
If ($cmdopts[0] > 2) Then
    For $a = 1 to ($cmdopts[0] - 2)
        Select
        Case StringLower($cmdopts[$a]) = "/y"
            $forceoverwrite = 1
        case StringLower(StringLeft($cmdopts[$a], 7)) = "/title:"
            $title = StringSplit($cmdopts[$a], ":")
            $title = $title[2]
        case $cmdopts[$a] = "/?"
            MsgBox(0, "Help", $helpmsg)
            Exit
        Case Else
            MsgBox(0, "Help", $helpmsg)
            Exit
        EndSelect
    Next    
EndIf
$source = $cmdopts[($cmdopts[0] -1)]
$destination = $cmdopts[($cmdopts[0])]

; Check that source file exist
If Not (FileExists($source)) Then
    MsgBox(0, "Error", $source & " file does not exist.")
    Exit
EndIf

; If destination exist, ask if ok to overwrite
If (FileExists($destination)) Then
    If ($forceoverwrite = 0) Then
        If (MsgBox(260, "Overwrite", $destination & " already exists.  Overwrite?") = 7) Then
            Exit
        EndIf
    EndIf
    If Not (FileDelete($destination)) Then
        MsgBox(0, "Error", "Could not copy to " & $destination)
        Exit
    EndIf
EndIf


; Get source file size
Dim $sourcesize = FileGetSize($source)
Dim $sourceprefix = 0
Dim $sourcesuffix = ""
getByteSuffix($sourcesize, $sourceprefix, $sourcesuffix)

; Copy file, querying process for IO bytes written
Dim $starttime = TimerInit()
Dim $destinationsize = 0
Dim $progress = 0
Dim $spin = "|"

; Display progress bar and run copy command
ProgressOn($title, "Please wait...", "Preparing...", -1, -1, 16)
Dim $pid = Run(@ComSpec & " /c copy /y " & chr(34) & $source & chr(34) & " " & Chr(34) & $destination & Chr(34), @ScriptDir, @SW_HIDE)
If ($pid = 0) Then
    MsgBox(0, "Error", "Error copying file")
    Exit
EndIf
While (ProcessExists($pid))
    Local $procstats = ProcessGetStats($pid, 1)
    If (IsArray($procstats) And UBound($procstats) > 4) Then
        $destinationsize = $procstats[4]
        If ($destinationsize = 0) Then
            ProgressSet(0, "", "Calculating size ...")
        Else
            $progress = Int(($destinationsize / $sourcesize) * 100)
            For $x = 1 to 10
                progressUpdate($progress, $destinationsize)     
                Sleep(100)
            Next
        EndIf
    EndIf
WEnd

; After copy finished, display destination file size
$destinationsize = FileGetSize($destination)
$progress = Int(($destinationsize / $sourcesize) * 100)
progressUpdate($progress, $destinationsize)
Sleep(500)
ProgressOff()

; Compare source file size and destination file size
If ($sourcesize <> $destinationsize) Then
    MsgBox(0, "Error", "Source and destination file sizes are different.  Could not copy.")
    Exit
EndIf

Exit

Func progressUpdate($progress, $destsize)   
    If ($progress > 100) Then
        $progress = 100
    EndIf
    Local $byteprefix = 0
    Local $bytesuffix = ""
    If (StringLen($spin) > 50) Then $spin = StringRight($spin, 1)
    Switch StringRight($spin, 1)
    Case "|"
        $spin = StringReplace($spin, "|", "/", 1, 2)
    Case "/"
        $spin = StringReplace($spin, "/", "-", 1, 2)
    Case "-"
        $spin = StringReplace($spin, "-", "\", 1, 2)
    Case "\"
        $spin = StringReplace($spin, "\", ".", 1, 2) & ".|"
    EndSwitch
    getByteSuffix($destsize, $byteprefix, $bytesuffix)
    ProgressSet($progress, "Source: " & $source & @LF & "Destination: " & $destination  & @LF & $spin, Round($byteprefix, 2) & " " & $bytesuffix & " / " & Round($sourceprefix, 2) & " " & $sourcesuffix)
EndFunc

Func getByteSuffix ($bytes, ByRef $byteprefix, ByRef $bytesuffix)
    $byteprefix = $bytes
    $bytesuffix = "bytes"
    If ($byteprefix > 1024) Then
        $byteprefix = ($byteprefix / 1024)
        $bytesuffix = "KB"
        If ($byteprefix > 1024) Then
            $byteprefix = ($byteprefix / 1024)
            $bytesuffix = "MB"
            If ($byteprefix > 1024) Then
                $byteprefix = ($byteprefix / 1024)
                $bytesuffix = "GB"
                If ($byteprefix > 1024) Then
                    $byteprefix = ($byteprefix / 1024)
                    $bytesuffix = "TB"
                    If ($byteprefix > 1024) Then
                        $byteprefix = ($byteprefix / 1024)
                        $bytesuffix = "PB"
                    EndIf
                EndIf
            EndIf
        EndIf
    EndIf
EndFunc

Hope this helps,

Doug

Link to comment
Share on other sites

  • 3 weeks later...
  • 3 weeks later...

i understand thta it was stated before that this scipt will work for copy the entire contents of the folder or maybe the folder itself. however i cannot get it to work, and i cannot read german, lol. thanks for your help.

$temp1 = "C:\test\temp1\*.*"
$temp2 = "C:\test\temp2"

; Need to use a large file >2MB
$FileCopy = _CopyWithProgress($temp1, $temp2)

; Check return code to see if file copied correctly
If $FileCopy = 0 Then
    MsgBox(0,"Copy Complete", "File Copy Successful.")
Else
    MsgBox(16,"Copy Complete", "File Copy Failed.")
EndIf


Func _CopyWithProgress($inSource, $inDest)

; Function to copy a file showing a realtime progress bar
;
; By Paul Niven (NiVZ)
;
; 05/11/2008
;
; Parameters:
;
; $inSource  -   Full path to source file
; $inDest   -    Full path to destination file (structure will be created if it does not exist
;
; Returns 0 if copy is successful and 1 if it fails
;
;
; Updates:
;
; 06/11/2008 - Now shows SOURCE and DEST on seperate lines while copying
;           - Now uses Kernal32 and DllCallBackRegister to provide faster copying
;             Kernal32 and DllCallBackRegister taken from _MultiFileCopy.au3
;             See: http://www.autoit.de/index.php?page=Thread&postID=58875


; Create dll hooks for Progress Update
    If Not IsDeclared("callback") Then Local $callback = DllCallbackRegister('__Progress', 'int', 'uint64;uint64;uint64;uint64;dword;dword;ptr;ptr;str')
    If Not IsDeclared("ptr") Then Local $ptr = DllCallbackGetPtr($callback)

; Check that file open was successful
    If Not FileExists($inSource) Then
        MsgBox(16, "Error", "Unable to open source file: " & $inSource)
        Exit
    EndIf

; Get Destination Directory - without filename
    $DestDir = StringLeft($inDest, StringInStr($inDest, "\", "", -1))

; Check destination directory exists and create it if it doesn't try to create it
    If Not FileExists($DestDir) Then
        $DirCreate = DirCreate($DestDir)
        If $DirCreate = 0 Then MsgBox(16, "Error", "Could not create destination directory: " & $DestDir)
    EndIf

; Show the progress bar
    ProgressOn("File Copy", "Copying...", "Source: " & $inSource & @LF & "Dest:  " & $inDest)

    $ret = DllCall('kernel32.dll', 'int', 'CopyFileExA', 'str', $inSource, 'str', $inDest, 'ptr', $ptr, 'str', '', 'int', 0, 'int', 0)

; Hide the progress bar
    ProgressOff()

    If $ret[0] <> 0 Then
    ; Success
        Return 0
    Else
    ; Fail
        Return 1
    EndIf

EndFunc


Func __Progress($FileSize, $BytesTransferred, $StreamSize, $StreamBytesTransferred, $dwStreamNumber, $dwCallbackReason, $hSourceFile, $hDestinationFile, $lpData)

    ProgressSet(Round($BytesTransferred / $FileSize * 100, 0))

EndFunc

i get an error: File Copy Failed

Link to comment
Share on other sites

Yahoo BabelFish should at least give you an idea about what is being discussed there. You can translate whole web pages on BabelFish.

good idea but does not work to well with forums, not for me. i do notice that they are talking about _MultiFileCopy not _CopyWithProgress. i just need a single copy but the entire folder. i like the idea of file details and speed performance.

Link to comment
Share on other sites

i understand thta it was stated before that this scipt will work for copy the entire contents of the folder or maybe the folder itself. however i cannot get it to work, and i cannot read german, lol. thanks for your help.

$temp1 = "C:\test\temp1\*.*"
$temp2 = "C:\test\temp2"

; Need to use a large file >2MB
$FileCopy = _CopyWithProgress($temp1, $temp2)

; Check return code to see if file copied correctly
If $FileCopy = 0 Then
    MsgBox(0,"Copy Complete", "File Copy Successful.")
Else
    MsgBox(16,"Copy Complete", "File Copy Failed.")
EndIf


Func _CopyWithProgress($inSource, $inDest)

; Function to copy a file showing a realtime progress bar
;
; By Paul Niven (NiVZ)
;
; 05/11/2008
;
; Parameters:
;
; $inSource  -   Full path to source file
; $inDest   -    Full path to destination file (structure will be created if it does not exist
;
; Returns 0 if copy is successful and 1 if it fails
;
;
; Updates:
;
; 06/11/2008 - Now shows SOURCE and DEST on seperate lines while copying
;           - Now uses Kernal32 and DllCallBackRegister to provide faster copying
;             Kernal32 and DllCallBackRegister taken from _MultiFileCopy.au3
;             See: http://www.autoit.de/index.php?page=Thread&postID=58875


; Create dll hooks for Progress Update
    If Not IsDeclared("callback") Then Local $callback = DllCallbackRegister('__Progress', 'int', 'uint64;uint64;uint64;uint64;dword;dword;ptr;ptr;str')
    If Not IsDeclared("ptr") Then Local $ptr = DllCallbackGetPtr($callback)

; Check that file open was successful
    If Not FileExists($inSource) Then
        MsgBox(16, "Error", "Unable to open source file: " & $inSource)
        Exit
    EndIf

; Get Destination Directory - without filename
    $DestDir = StringLeft($inDest, StringInStr($inDest, "\", "", -1))

; Check destination directory exists and create it if it doesn't try to create it
    If Not FileExists($DestDir) Then
        $DirCreate = DirCreate($DestDir)
        If $DirCreate = 0 Then MsgBox(16, "Error", "Could not create destination directory: " & $DestDir)
    EndIf

; Show the progress bar
    ProgressOn("File Copy", "Copying...", "Source: " & $inSource & @LF & "Dest:  " & $inDest)

    $ret = DllCall('kernel32.dll', 'int', 'CopyFileExA', 'str', $inSource, 'str', $inDest, 'ptr', $ptr, 'str', '', 'int', 0, 'int', 0)

; Hide the progress bar
    ProgressOff()

    If $ret[0] <> 0 Then
    ; Success
        Return 0
    Else
    ; Fail
        Return 1
    EndIf

EndFunc


Func __Progress($FileSize, $BytesTransferred, $StreamSize, $StreamBytesTransferred, $dwStreamNumber, $dwCallbackReason, $hSourceFile, $hDestinationFile, $lpData)

    ProgressSet(Round($BytesTransferred / $FileSize * 100, 0))

EndFunc

i get an error: File Copy Failed

Yeah me too, Cannot figure it out yet.
Link to comment
Share on other sites

  • 2 months later...
  • 3 years later...

When I run the script I get unable to open file. Here is the script with my file path.

I tried using a txt JPG and MP3 file thinking it was a file format issue.

Am I missing something ?

#cs ----------------------------------------------------------------------------

AutoIt Version: 3.3.8.1

Author: myName

Script Function:

Template AutoIt script.

#ce ----------------------------------------------------------------------------

; Script Start - Add your code below here

$Test1 = "C:\Users\John\Desktop\Test1\1.mp3"

$Test2 = "C:\Users\John\Desktop\Test2"

; Need to use a large file >2MB

$FileCopy = _CopyWithProgress($Test1, $Test2)

; Check return code to see if file copied correctly

If $FileCopy = 0 Then

MsgBox(0,"Copy Complete", "File Copy Successful.")

Else

MsgBox(16,"Copy Complete", "File Copy Failed.")

EndIf

Func _CopyWithProgress($inSource, $inDest)

; Function to copy a file showing a realtime progress bar

;

; By Paul Niven (NiVZ)

;

; 05/11/2008

;

; Parameters:

;

; $inSource - Full path to source file

; $inDest - Full path to destination file (structure will be created if it does not exist

;

; Returns 0 if copy is successful and 1 if it fails

;

;

; Updates:

;

; 06/11/2008 - Now shows SOURCE and DEST on seperate lines while copying

; - Now uses Kernal32 and DllCallBackRegister to provide faster copying

; Kernal32 and DllCallBackRegister taken from _MultiFileCopy.au3

; See: http://www.autoit.de/index.php?page=Thread&postID=58875

; Create dll hooks for Progress Update

If Not IsDeclared("callback") Then Local $callback = DllCallbackRegister('__Progress', 'int', 'uint64;uint64;uint64;uint64;dword;dword;ptr;ptr;str')

If Not IsDeclared("ptr") Then Local $ptr = DllCallbackGetPtr($callback)

; Check that file open was successful

If Not FileExists($inSource) Then

MsgBox(16, "Error", "Unable to open source file: " & $inSource)

Exit

EndIf

; Get Destination Directory - without filename

$DestDir = StringLeft($inDest, StringInStr($inDest, "\", "", -1))

; Check destination directory exists and create it if it doesn't try to create it

If Not FileExists($DestDir) Then

$DirCreate = DirCreate($DestDir)

If $DirCreate = 0 Then MsgBox(16, "Error", "Could not create destination directory: " & $DestDir)

EndIf

; Show the progress bar

ProgressOn("File Copy", "Copying...", "Source: " & $inSource & @LF & "Dest: " & $inDest)

$ret = DllCall('kernel32.dll', 'int', 'CopyFileExA', 'str', $inSource, 'str', $inDest, 'ptr', $ptr, 'str', '', 'int', 0, 'int', 0)

; Hide the progress bar

ProgressOff()

If $ret[0] <> 0 Then

; Success

Return 0

Else

; Fail

Return 1

EndIf

EndFunc

Func __Progress($FileSize, $BytesTransferred, $StreamSize, $StreamBytesTransferred, $dwStreamNumber, $dwCallbackReason, $hSourceFile, $hDestinationFile, $lpData)

ProgressSet(Round($BytesTransferred / $FileSize * 100, 0))

EndFunc

post-77883-0-20415400-1359685350_thumb.j

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
 Share

  • Recently Browsing   0 members

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