Even if we are living in a cloud era, the older technologies are not dead yet. Large organizations are often masters if these older technologies which they learned to trust. The File Transfer Protocol, aka FTP, is one of these when comes time to share files between organizations. Even individuals are using it where emails are not an option.
FTP, SFTP, FTPES
Those are all very similar acronyms but they are not the same.
Let starts with SFTP. The S in SFTP doesn’t stand for secure. The real meaning is SSH File Transfer protocol. But transferring files over SSH secures the connection by encrypting the data before putting it on the wire. The FtpWebRequest class of .Net does not let you connect to sites using this protocol.
Then comes FTP, the regular one that everybody has dealt with one day or another to download a file from a 3rd party component or any other files. It can be easily accessed simply by using Windows Explorer. The folder that you are reaching may be password protected but the password alone does not provide the S to the protocol, it simply tries to protect unauthorized people from accessing your files! When using this protocol, everything sent on the wire (including credentials such as user name and password) are sent in clear text and can be seen by anyone sniffing the line.
And finally FTPES, the one we will explore in this article that has all the tools in .Net. It is not a protocol per say. It is an encryption over a regular FTP connection. That means that everything sent on the wire is then using Secure Sockets Layer (SSL).
Downloadable demo
The downloadable code this month is provided in both VB and C#. It has been created using Visual Studio 2013 targeting the .Net Framework 4.5.1 but the main code would also work starting with the .Net Framework 2.0.
The demo application let you list files of the current folder on the server, upload a new file to the server, and download a file from the server (but does not save, just display in a message box).
Figure 1: The demo application in action (URI, user name and password are fake!)
Validate your connection with a tool
A very popular and free tool that provides you a console to connect to any flavor of FTP sites is Filezilla. You can get it from https://filezilla-project.org/.
It will be a lot easier if connect to your FTP site first with any tool to prove that it is working. You will then be able to validate that you are using the right protocol, that your URI is valid, that your credentials are valid, and that you are using the correct encryption mechanism (if any).
Establishing a connection
After you have used a tool like Filezilla to confirm all your connection properties, you are ready to establish a connection.
In an effort not to repeat myself because the code is mostly the same, I have created a method to establish a connection to the FTP site (the GetFTPRequest method).
When you want to create a connection using the FtpWebRequest object, you need to specify the kind of action you will try to do (list files, upload files, download files, …). This is done by setting the Method property.
Private Shared Function GetFTPRequest(ByVal pUri As String, ByVal pUser As String, ByVal pPwd As String, ByVal pMethod As String) As FtpWebRequest Dim ftpRequest As FtpWebRequest = TryCast(WebRequest.Create(New Uri(pUri)), FtpWebRequest) ftpRequest.Method = pMethod ftpRequest.EnableSsl = True ftpRequest.Credentials = New NetworkCredential(pUser, pPwd) ftpRequest.UsePassive = True ftpRequest.Timeout = Threading.Timeout.Infinite ftpRequest.KeepAlive = True ftpRequest.UseBinary = True '//Hook a callback to verify the remote certificate ServicePointManager.ServerCertificateValidationCallback = AddressOf ValidateServerCertificate Return ftpRequest End Function
By simply setting the EnableSsl property to true, you are specifying that the connection should use encryption but the server has to support it. Again, it isn’t SFTP (which isn’t supported by the FtpWebRequest class), it is rather FTPES.
Any property not correctly set will simply fail the request to connect with errors that are sometime cryptic. This is why it often easier to validate your parameters with a tool you know working.
One important thing when establishing a connection to a FTPES server is the validation of the server certificate. You normally validate the subject string. This will ensure that you are connected to the server you really want. This validation is done by setting a callback event (as shown in the GetFTPRequest method):
Private Function ValidateServerCertificate(ByVal sender As Object, ByVal certificate As X509Certificate, ByVal chain As X509Chain, ByVal sslPolicyErrors As SslPolicyErrors) As Boolean 'simple validation with the subject If (certificate.Subject.Contains(txtCertificate.text)) Then Return True Else Return False End If End Function
This method is not handling any errors. They are simply bubbled up to the caller. Your final application will need to handle a bunch of exceptions here.
Listing existing files
After a connection has been established to the FTP site, you can easily list the files hosted there. This is the purpose of “List files” button (which calls the FTPListFiles method). The existing files are listed in the listbox along with the file time stamp and the size of the file (in bytes).
This is the code of the FTPListFiles method returning a list of string that will then be displayed in the listbox control:
Private Function FTPListFiles(ByVal pUri As String, ByVal pUser As String, ByVal pPwd As String) As IEnumerable(Of String) Try Dim fileList As New List(Of String) Dim ftpRequest As FtpWebRequest = GetFTPRequest(pUri, pUser, pPwd, WebRequestMethods.Ftp.ListDirectoryDetails) Dim response As FtpWebResponse = TryCast(ftpRequest.GetResponse(), FtpWebResponse) Dim reader As StreamReader = New StreamReader(response.GetResponseStream()) Dim line As String = reader.ReadLine() Do While (Not String.IsNullOrWhiteSpace(line)) fileList.Add(line) line = reader.ReadLine() Loop Return fileList Catch ex As Exception MessageBox.Show(ex.Message.ToString()) Return Nothing End Try End Function
A good idea if you have a new FTP site and it is empty, while trying your connection parameters, upload a first file so the list won’t be empty. Upload a plain old text file. You will find out why later.
Uploading a file
The list should now show at least a file if you followed my suggestion of uploading a file while testing your connection settings. Now it is time to upload another one using code.
The Upload button will show a dialog letting the user pick a file and that file will be uploaded (by calling the BrowseForFile method). Again, pick a plain old text file.
The code of the FTPUpload method reads like this:
Private Function FTPUpload(ByVal pUri As String, ByVal pUser As String, ByVal pPwd As String, ByVal pFilename As String) As Boolean Try Dim strFileWithoutPath As String = Path.GetFileName(pFilename) Dim ftpRequest As FtpWebRequest = GetFTPRequest(pUri + "/" + strFileWithoutPath, pUser, pPwd, WebRequestMethods.Ftp.UploadFile) '// Copy the contents of the file to the request stream. Dim sourceStream As New StreamReader(pFilename) Dim fileContents As Byte() = Encoding.UTF8.GetBytes(sourceStream.ReadToEnd()) sourceStream.Close() ftpRequest.ContentLength = fileContents.Length Dim requestStream As Stream = ftpRequest.GetRequestStream() requestStream.Write(fileContents, 0, fileContents.Length) requestStream.Close() Dim response As FtpWebResponse = TryCast(ftpRequest.GetResponse(), FtpWebResponse) MessageBox.Show("Upload File Complete, status {0}", response.StatusDescription) response.Close() Return True Catch ex As Exception MessageBox.Show(ex.Message.ToString()) Return False End Try End Function
This code is using the GetRequestStream which returns the stream used to upload data to an FTP server. Once we have this stream, we simply write to it like any other stream and the file will be uploaded.
Once the upload has been completed, I call the PerformClick event of the list files button to refresh the content of the listbox.
Downloading a file
To download a file, pick a file from the listbox and click the download button.
Downloading a file is done using another stream. When you open the Request object, you need to pass the name of the file and call the GetResponse method. On this Response object, you just have to call the GetResponseStream to return a stream. Finally, on that stream, just call ReadToEnd and the file will be downloaded.
The code of the FTPDownload method reads like this:
Private Sub FTPDownload(ByVal pUri As String, ByVal pUser As String, ByVal pPwd As String, ByVal pFilename As String) Try Dim ftpRequest As FtpWebRequest = GetFTPRequest(pUri + "/" + pFilename, pUser, pPwd, WebRequestMethods.Ftp.DownloadFile) Dim response As FtpWebResponse = TryCast(ftpRequest.GetResponse(), FtpWebResponse) Dim reader As StreamReader = New StreamReader(response.GetResponseStream()) Dim content As String = reader.ReadToEnd() MessageBox.Show("Download File Complete, status {0}", response.StatusDescription) MessageBox.Show("The file content is: " + content) reader.Close() response.Close() Catch ex As Exception MessageBox.Show(ex.Message.ToString()) End Try End Sub
This demo application does not save the file on disk. Instead, it is showing the file content in a message box. This is why I insisted that you upload a text file to the FTP server.
Other components
I have provided you code here that is 100% pure .Net. It does the job correctly but it falls short on many aspect. For example, this code opens a connection dedicated to a single type of operation (list, upload, download, …). The same connection cannot be shared if you want to do multiple operations.
If you are serious about building an application that connect to any flavor of FTP sites, it might be worth investing in a 3rd party component. There are many out there. I have used Xceed with great success. The object model is simpler and the feature set is richer.
Conclusion
It is somewhat easy to communicate with a FTP and/or FTPES server and the .Net Framework provides everything you need.