Please use FTP Class instead!
Whats better?
- All the functions wrapped into a class (NO global variables)
- Multiple FTP connections possible with same class
- Asynchronous mode is now fully functional! (Read notes in docs)
- No need to call .Close(), clean-up is done when ftp object is deleted.
================== ORIGINAL POST BELOW ==================
Original FTP Functions by Olfen & Andreone - thread10393, modified by ahklerner - this post
Modified by me for AHK_L,
- added object syntax
- added GetCurrentDirectory()
- corrected FileTime retrieval from Win32_Find_Data structure
- created documentation (thanks fincs for GenDocs and Scite4Autohotkey)
Update 2011-01-17:
- Added FTP_Init(), which needs to be called first to retrieve Object reference
- Error/Extended Error (if any) is retrieved in human readable form [.LastError property]
- FTP connection properties can be set (see documentation)
- Uses only one global variable (an object)
Update 2011-01-21:
- Added .InternetReadFile() and .InternetWriteFile() methods
- Progress indicator for uploads and downloads
Update 2011-02-03:
- Bugfixes (bug) and improvements
- FTP_Init() now has two optional parameters - Proxy and ProxyBypass
- .CloseSocket() function documented
Update 2011-02-20:
- Supports x64 also (thanks fragman)
Update 2011-03-01:
- Asynchronous mode added (Alpha) (see Asynchronous mode notes before using).
- FTP_Init() has new parameter AsyncMode
- .Open() now returns handle, .Close() accepts handle as parameter.
May not work correctly with ANSI build. No support for multiple FTP sessions
Documentation |Download
Example:
; initialize and get reference to FTP object ftp := FTP_Init() ; connect to FTP server if !ftp.Open("ftp.autohotkey.net", "myUserName", "myPassword") { MsgBox % ftp.LastError ExitApp } ; get current directory sOrgPath := ftp.GetCurrentDirectory() if !sOrgPath MsgBox % ftp.LastError ;; Error handling omitted from here on for brevity ; upload a file with progress ftp.InternetWriteFile( A_ScriptDir . "\FTP.zip" ) ; download a file with progress ftp.InternetReadFile( "FTP.zip" , "delete_me.zip") ; delete the file ftp.DeleteFile("FTP.zip") ; create a new directory 'testing' if !ftp.CreateDirectory("testing") MsgBox % ftp.LastError ;; Error-checking code omitted from here on for brevity ; set the current directory to 'root/testing' ftp.SetCurrentDirectory("testing") ; upload this script file ftp.PutFile(A_ScriptFullPath, A_ScriptName) ; rename script to 'mytestscript.ahk' ftp.RenameFile(A_ScriptName, "MyTestScript.ahk") ; enumerate the file list from the current directory ('root/testing') item := ftp.FindFirstFile("/testing/*") MsgBox % "Name : " . item.Name . "`nCreationTime : " . item.CreationTime . "`nLastAccessTime : " . item.LastAccessTime . "`nLastWriteTime : " . item.LastWriteTime . "`nSize : " . item.Size . "`nAttribs : " . item.Attribs Loop { if !(item := FTP_FindNextFile()) break MsgBox % "Name : " . item.Name . "`nCreationTime : " . item.CreationTime . "`nLastAccessTime : " . item.LastAccessTime . "`nLastWriteTime : " . item.LastWriteTime . "`nSize : " . item.Size . "`nAttribs : " . item.Attribs } ; retrieve the file from the FTP server ftp.GetFile("MyTestScript.ahk", A_ScriptDir . "\MyTestScript.ahk", 0) ; delete the file from the FTP server ftp.DeleteFile("MyTestScript.ahk") ; set the current directory back to the root ftp.SetCurrentDirectory(sOrgPath) ; remove the direcrtory 'testing' ftp.RemoveDirectory("testing") ; close the FTP connection, free library ftp.Close() #Include FTP.ahk
Asynchronous example:
; initialize and get reference to FTP object ftp := FTP_Init(1) OnExit, Cleanup ; connect to FTP server if !ftp.Open("ftp.autohotkey.net", "myUserName", "myPassword") { MsgBox % ftp.LastError ExitApp } SleepWhile() ; create a new directory 'testing' if !ftp.CreateDirectory("testing") MsgBox % ftp.LastError if !SleepWhile() MsgBox CreateDirectory failed ;; Error-checking code omitted from here on for brevity ; set the current directory to 'root/testing' ftp.SetCurrentDirectory("testing") if !SleepWhile() MsgBox SetCurrentDirectory failed ; upload this script file ftp.PutFile(A_ScriptFullPath, A_ScriptName) if !SleepWhile(0,"FTP_Progress") MsgBox PutFile failed ; rename script to 'testscript.ahk' ftp.RenameFile(A_ScriptName, "TestScript.ahk") if !SleepWhile() MsgBox Rename failed ftp.File.BytesTotal := ftp.GetFileSize("TestScript.ahk") ;no need to wait for .GetFileSize IfExist, %A_ScriptDir%\TestScript.ahk FileDelete, %A_ScriptDir%\TestScript.ahk ; retrieve the file from the FTP server ftp.GetFile("TestScript.ahk", A_ScriptDir . "\TestScript.ahk", 0) if !SleepWhile(0,"FTP_Progress") MsgBox GetFile failed ; delete the file from the FTP server ftp.DeleteFile("TestScript.ahk") if !SleepWhile() MsgBox DeleteFile failed ; set the current directory back to the root ftp.SetCurrentDirectory("/") if !SleepWhile() MsgBox SetCurrentDirectory failed ; remove the directory 'testing' ftp.RemoveDirectory("testing") if !SleepWhile() MsgBox RemoveDirectory failed Cleanup: ; close the FTP connection, free library ftp.Close() sleep 1000 ;The request complete will not be triggered, as the last message recieved is 70 = INTERNET_STATUS_HANDLE_CLOSING exitapp SleepWhile(WaitSeconds=0,ShowProgress=0) { global ftp if !WaitSeconds WaitSeconds := 20 WaitIndex := WaitSeconds * 1000 / 20 if ShowProgress { if ShowProgress is Integer ShowProgress := "FTP_Progress" } While !ftp.AsyncRequestComplete { sleep 20 if ShowProgress { %ShowProgress%() continue } if A_Index > %WaitIndex% ;20 seconds by default { MsgBox, 36, Asynchronous Operation, The asyncrhonous operation is taking too long to complete. Abort script? IfMsgBox, Yes ExitApp } } return (ftp.AsyncRequestComplete = 1) ? 1 : 0 ; -1 means request complete but failed, only 1 is success } #Include FTP.ahk
MSDN Reference: FTP Sessions (Windows)
Uploading - Use PutFile for small files only (otherwise program will be unresponsive till the DllCall is complete). For large files, use InternetWriteFile. If you do not specify a function to handle progress (optional third parameter), upload shows a borderless progress window.
Downloading - Use GetFile for small files only (otherwise program will be unresponsive till the DllCall is complete). For large files, use InternetReadFile. If you do not specify a function to handle progress (optional third parameter), upload shows a borderless progress window.
.CloseSocket() - Call to close only current FTP session. Open a new session with .Open
.Close() - Call when you are done using this FTP library.
This feature is still in alpha stage of development.
The default callback function outputs status related information to console only.
Functions which use data buffers will not work in asynchronous mode.
(Not because it is not possible in AHK, but because it is not desirable* and beyond my skill level)
These functions include:
 - .GetCurrentDirectory()
 - .FindFirstFile / .FindNextFile
 - .InternetReadFile / .InternetWriteFile
* Reference: INFO: Using WinInet APIs Asynchronously Within Visual Basic
To quote : "This makes using WinInet APIs in Visual Basic asynchronously an undesirable option."
Callback function: Note thatyou can specify the function to call (AsyncMode parameter can be the name of the function). Because callbacks are made during processing of the request, the application should spend little time in the callback function to avoid degrading data throughput on the network. For example, displaying a msgbox in a callback function can be such a lengthy operation that the server terminates the request.
Memory/File operations: In your script, please do not write to the memory/file that has set up to use in the callback function. Both the script and wininet callback may try to write to the same memory location/file and corrupt the file/memory or crash the script.
AHK and multithreading: As AHK uses psuedo-multithreading (it is a single thread only), wininet callbacks will pause the currently executing thread. If the current executing thread is critical, the async notifications may be missed.
Anybody can take a shot at improving the asynchronous mode.