Hi all,
I've been doing some work over the last few days adding improved FTP support to Web Conenction and the Client Tools. This brings more features and consistency across FTP protocols to FTP processing.
There are two new classes:
- wwFtpClient - Replaces wwFtp and provides support for FTP and SFTP
- wwSftpClient - Replaces wwSftp and provides support for SFTP
Both classes now have the same exact interface (except for a few SFTP specific connection features). Both classes are now simpler in that they provide just the Connect()
, Ftp Operations, Close()
model of operation. In essence the new interface dumps all the funky legacy that was based around the old Win32 APIs that were used with the old WinInet based libraries.
All the same features are still available:
- Download
- Upload
- ListFiles
- DeleteFile
- MoveFile
- CreateDirectory
- DeleteDirectory
- ChangeDirectory
An Example
The following code is an example that demonstrates most of the functionality in a single short program that uploads, downloads, lists and deletes files on an FTP server using FTPS:
loFtp = CREATEOBJECT("wwFtpClient")
loFtp.lUseTls = .T.
loFtp.cLogFile = "c:\temp\ftp.log" && verbose log - leave empty
oFtp.lIgnoreCertificateErrors = .T. && self-signed cert not installed
SET PROCEDURE TO wwutils ADDITIVE && for UI helpers
*** yourserver.com or yourserver.com:28
lcServer = INPUTBOX("Server Domain/IP")
IF EMPTY(lcServer)
RETURN
ENDIF
lcUsername = InputBox("User name")
IF EMPTY(lcUsername)
RETURN
ENDIF
lcPassword = GetPassword("Password")
IF EMPTY(lcPassword)
RETURN
ENDIF
loFtp.oProgressEventObject = CREATEOBJECT("FtpClientProgressEvents")
loFtp.cServer = lcServer
loFtp.cUsername = lcUsername
loFtp.cPassword = lcPassword
*loFtp.nPort = 21 && only needed if custom port is required
IF !loFtp.Connect()
? loFtp.cErrorMsg
RETURN
ENDIF
? "Connected to " + lcServer
IF !loFtp.DownloadFile("Tools/jsMinifier.zip", "c:\temp\jsMinifier.zip")
? loFtp.cErrorMsg
RETURN
ENDIF
? "Downloaded " + "Tools/jsMinifier.zip"
lcUploadFile = "Tools/jsMinifier" + SYS(2015) + ".zip"
IF !loFtp.UploadFile("c:\temp\jsMinifier.zip", lcUploadFile)
? loFtp.cErrorMsg
RETURN
ENDIF
? "Uploaded " + lcuploadFile
*** provide a folder name (no wildcards)
loCol = loFtp.ListFiles("/Tools")
? TRANSFORM(loCol.Count ) + " matching file(s)"
? loFtp.cErrorMsg
FOR EACH loFile IN loCol FOXOBJECT
IF ( AT("jsMinifier_",loFile.Name) = 1)
? loFtp.oBridge.ToJson(loFile) && for kicks print out as json
IF loFtp.DeleteFile(loFile.FullName)
? "Deleted " + loFile.FullName
ENDIF
ENDIF
ENDFOR
RETURN
DEFINE class FtpClientProgressEvents as Custom
FUNCTION OnFtpBufferUpdate(lnPercent, lnDownloadedBytes, lcRemotePath, lcMode)
lcMsg = lcMode + ": " + TRANSFORM(lnPercent) + "% complete. " + lcRemotePath + " - " + TRANSFORM(lnDownloadedBytes) + " bytes"
? "*** " + lcMsg
ENDFUNC
ENDDEFINE
Under the Covers
The FTP and SFTP classes now use a .NET component (Fluent FTP) for handling the FTP interaction. The main reason for the change is support for FTPS (FTP over TLS) which seems to be very common since many OSS servers like FileZilla use it and unsecured FTP is no longer a viable option for anything but internal solutions.
The SFTP class continues to use the same .NET component it's been using (SSH.net) but the functionality has been simplified to match the FTP/SFTP class. The .NET component also has been updated to the latest 2024 release.
Breaking Changes, but old versions continue to work
These new libraries are a breaking change obviously, but the old libraries will continue to ship in the OldFiles
folder. The only breaking change for the old version is the OnFtpBufferUpdate()
functionality which has been changed at the API interface level.
+++ Rick ---
