(Print this page)

Connecting to FTP & FTPES using .Net
Published date: Sunday, October 26, 2014
On: Moer and Éric Moreau's web site

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.


(Print this page)