Click here to Skip to main content
Email Password   helpLost your password?

Introduction

One annoying omission from the 1.x framework for .NET was support for FTP. This could be rectified by various libraries (some free, others commercial) that filled this gap. However, with Visual Studio 2005 and 2.0 of the .NET framework, FTP makes a welcome appearance.

As well as adding FTP, Microsoft has moved support for web, mail and FTP requests out of System.Web and into System.Net which is a more logical approach.

There is still a problem however: the FTP support isn't actually an FTP client, it's just support for the protocol in FtpWebRequest, in the same way as HttpWebRequest supports web requests. There is no "download a file" or "get a directory listing" function - you're still left to sort this out yourself.

This is where I hope my library FTPclient will come in useful. It's not a full-featured and comprehensive client but it provides all the most frequently used functions and can act as a base to add any missing ones if you need them.

Background

I assume here that you've got .NET 2.0 or one of the betas. This library was written on beta 2 of VS2005, so if you have a later version or the released version some changes may be required. I'll try to update the code if any framework changes break it.

I wrote this library to support my own application which needed to upload and download files to a supplier's FTP server: this runs on Linux, but I also tested it against the Microsoft FTP server that comes with NT and XP.

FTPClient Design

FTPclient is designed to operate in a stateless mode, in a similar way to how a web request would work. It does not hold open a connection but instead will connect, perform the requested action, and disconnect for each request.

This does mean it's very suitable for single-action operations but not ideal in performance terms if you want to hold open a connection while performing multiple requests. However the library could be adapted to operate in this way if someone is willing to take the time.

FtpWebRequest Basics

Making any type of FTP requests can be broken down into six steps:

Although this might seem simple enough, there are several problems that can catch you out (they did for me!). One is that FtpWebRequest can support connections using the KeepAlive property, which is set to True by default. In my class it's turned off so that each connection is closed once the command completes.

An Example: Download a file

Here is an example of the steps in action, using FtpWebRequest to download a file:

'Values to use

Const localFile As String = "C:\myfile.bin"
Const remoteFile As String = "/pub/myftpfile.bin"
Const host As String = "ftp://ftp.myhost.com"
Const username As String = "myuserid"
Const password As String = "mypassword"

'1. Create a request: must be in ftp://hostname format, 

'   not just ftp.myhost.com

Dim URI As String = host & remoteFile
Dim ftp As System.Net.FtpWebRequest = _
    CType(FtpWebRequest.Create(URI), FtpWebRequest)

'2. Set credentials

ftp.Credentials = New _
    System.Net.NetworkCredential(username, password)

'3. Settings and action

ftp.KeepAlive = False
'we want a binary transfer, not textual data

ftp.UseBinary = True
'Define the action required (in this case, download a file)

ftp.Method = System.Net.WebRequestMethods.Ftp.DownloadFile

'4. If we were using a method that uploads data e.g. UploadFile

'   we would open the ftp.GetRequestStream here an send the data


'5. Get the response to the Ftp request and the associated stream

Using response As System.Net.FtpWebResponse = _
      CType(ftp.GetResponse, System.Net.FtpWebResponse)
  Using responseStream As IO.Stream = response.GetResponseStream
    'loop to read & write to file

    Using fs As New IO.FileStream(localFile, IO.FileMode.Create)
      Dim buffer(2047) As Byte
      Dim read As Integer = 0
      Do
        read = responseStream.Read(buffer, 0, buffer.Length)
        fs.Write(buffer, 0, read)
      Loop Until read = 0 'see Note(1)

      responseStream.Close()
      fs.Flush()
      fs.Close()
    End Using
    responseStream.Close()
  End Using
  response.Close()
End Using

'6. Done! the Close happens because ftp goes out of scope

'   There is no .Close or .Dispose for FtpWebRequest

Note (1): I found that using Loop Until read < buffer.Size does not work because sometimes data less than the buffer size is returned by a remote server, and it was possible to have this condition true before the end of the stream is reached. I found that read = 0 seems to only occur once the stream is finished.

In this particular example, steps 1 and 2 would be repeated for any type of FTP operation in the same way, so I put them into a function that can be re-used. Step 3 is largely dependent on the operation you will perform, as is the type of upload or download, but I created a generic function GetResponseString that will read a textual response (e.g. a directory listing). This code also lacks any error handling.

Using FtpClient

To use FtpClient, create a new instance of the object, defining the host, username and password.

Dim myFtp As New FtpClient(hostname, username, password)

To get a directory listing of the FTP server's /pub directory:

Dim fullList As FtpDirectory = myFtp.GetDirectoryDetail("/pub/")

To determine which of these are files, use the GetFiles function:

Dim filesOnly As FtpDirectory = fullList.GetFiles()

To download or upload a file - a simple example:

myFtp.Download("/pub/myfile.bin", "C:\myfile.bin")
myFtp.Upload("C:\myfile.bin", "/pub/myfile.bin")

Or a more complex example, downloading all the files from a directory.

For Each file As FtpFileInfo In myFtp.GetDirectoryDetail("/pub/").GetFiles
    myFtp.Download(file, "C:\" & file.Filename)
Next file

If a target file already exists for either uploads or downloads, the client will throw an exception by default to prevent unwanted overwrites. To turn off this behaviour, set the last, optional parameter PermitOverwrite to True.

Reading FTP Directories

Reading an FTP directory is simple enough: use either ListDirectory or ListDirectoryDetails request methods. ListDirectory is very simple - it returns a List(Of String) - but there is no distinction between a file or directory entry in the list so it may not be of use in most cases.

ListDirectoryDetails provides a lot more information about each file. It uses the detailed FTP listing which returns a collection of FtpFileInfo objects. An FtpFileInfo object contains the full path, name, date/time and file size of the entry as read from the detailed directory listing, in a similar way to FileInfo from System.IO.

Detailed FTP directory listings output varies according to the FTP server and the operating system it runs on. In particular, the NT/XP FTP server can be very different to UNIX and Linux results. The constructor for FtpFileInfo takes the text of the listing as a parameter and attempts to parse this with several regular expression patterns (held in _ParseFormats).

If you have errors with a particular FTP server reading detailed directories, you may need to add your own regular expressions to the _ParseFormats string array to get the library to work. I would expect the ones provided will work with most servers. Let me know if you find any new patterns that are needed.

Current Directory

I included the capability to store a current directory in the design in the same style as a standard FTP client application, although I've not used this myself. To set the directory, use FtpClient.CurrentDirectory = "/path". This comes into play if you don't specify a path for a remote file.

    Dim myFtp As New FtpClient(hostname, username, password)
    myFtp.CurrentDirectory = "/pub"
    myFtp.Download("fileInPub.bin", "C:\test\fileInPub.bin")
    
    myFtp.CurrentDirectory = "/pub/etc"
    'will upload to file /pub/etc/MyFile.bin

    myFtp.Upload("C:\MyFile.bin")

Possible Improvements

As mentioned already, this client does not support using an open connection, and has to log in for each request. In my application this wasn't a big issue, and I found the support for keeping the connection alive and performing multiple requests inadequately documented, so I decided to KISS (Keep It Simple).

Another improvement could be adding support for asynchronous operations which the FtpWebRequest object supports, but again KISS prevailed in my project.

Anyway, I hope you find this a useful little library that should do the basic operations you need for FTP.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionWorks perfect, but 1 question
Berry14
5:59 3 Mar '10  
Hi,
I am making a program to upload mutiple files. I can do that, but i'd like to do is asynchronous. So i can use a progressbar. Is that possible and how?

Many thanks!!!Thumbs Up
AnswerRe: Works perfect, but 1 question
Howard Richards
1:26 4 Mar '10  
The .NET FTP library does support asynchronous operation I think (from memory) but it is not used/implemented in this library - sorry
'Howard

GeneralNotes on Issues that I had
Tim.Faraday
7:30 25 Feb '10  
Hello,

First, this is a fantastic class and exactly what I was looking for. I did have to tweak it a little bit and I thought I would share the tweaks.

1 - HTTP Proxy. I kept getting an error when uploading a file saying that this command was not supported through an HTTP Proxy. I dug through and found that when an HTTP Proxy is present, you must explicitly set the FTPWebRequest.Proxy = nothing and the error went away.

I added this line to the GetRequest Function. "result.proxy = nothing" and my problems went away.

2 - Upload confirmation. I found that even when the file didn't actually transmit, that the Upload class would return true indicating that it had successfully uploaded.

I added this bit of code to the end of the Upload Function.

'Test to see if the file has uploaded completely.
If Me.GetFileSize(target) <> fi.Length Then

Return False

Else

Return True

End If

This was a great class and except for those two issues (quickly resolved) it has performed flawlessly. the comments in the class library are excellent.

Thanks!
- Jonathan
GeneralRe: Notes on Issues that I had
Howard Richards
23:04 10 Mar '10  
Thanks for the suggestions.. if you are on Codeplex can you submit them to the codeplex FtpClient project as amendments? We could then include them in the main code.
'Howard

GeneralI have gotten this working for MonoTouch for iPhone -
Jim Bentley
11:16 15 Feb '10  
I've gotten this working with Monotouch for iPhone - you can see a how-to here:

http://wiki.monotouch.net/How_to_compile_SharpSSH_C%23_library_for_SSH_and_SCP_communications_in_MonoTouch
Awesome work on this project - it's incredibly helpful.

Thanks!

Jim Bentley
GeneralRe: I have gotten this working for MonoTouch for iPhone -
Howard Richards
23:03 10 Mar '10  
No problems. Glad it was of use. And well done on getting it to run on an iPhone!
'Howard

QuestionMultiple File Upload
prithirajsengupta
12:04 8 Feb '10  
The component you made is really good. Thanks for this.

'6. Done! the Close happens because ftp goes out of scope

So, the connection also get disconnected if the ftp goes out of scope?

I am working for a scenario where I have to upload multiple data. So, I thought to modify

result.KeepAlive=True

GeneralUpdated FTP Library with Full Functional Application [modified]
Pathachiever
10:08 4 Feb '10  
Hi,
I recently posted an article that has an updated version of this Library.
I add new features to this library:
1) Download Progress Changed Event with Percentage
2) Download Completed Event
3) Upload Progress Changed Event with Percentage
4) Upload Completed Event
3) Changed some code and fixed a bug or two.
4) MessageReceived Event - with COMMAND and RESPONSE technique like FileZilla
5) Raise Event when New Message Received
6) Cancel Download
7) Cancel Upload
8) Welcome Message

I also implemented these features in a Full Functional application.
Check it out here: Windows Ftp Application[^]
**NEW: Support for all Windows Operating Systems has been added.

Thanks,
modified on Friday, February 12, 2010 9:06 PM

GeneralAdded FTPDirExists()
jtokach
12:33 18 Jan '10  
 ''' <summary>
        ''' Determine if dir exists on remote FTP site
''' </summary> ''' <param name="dirname">Dirname (for current dir) or full path</param> ''' <returns></returns> ''' <remarks></remarks> Public Function FtpDirExists(ByVal dirname As String) As Boolean 'Try to obtain dir listing: if we get error msg containing "550"
'the dir does not exist
Try Dim dirList As FTPdirectory = Me.ListDirectoryDetail(dirname)
Return True
Catch ex As Exception
'only handle expected not-found exception
If TypeOf ex Is System.Net.WebException Then 'file does not exist/no rights error = 550
If ex.Message.Contains("550") Then 'clear
Return False Else Throw End If Else Throw End If End Try End Function

GeneralRe: Added FTPDirExists()
Howard Richards
23:02 10 Mar '10  
Thanks for the code - hope others find it useful.

One suggestion: when catching exceptions you can specify the type rather than just Exception, e.g:

Catch ex as System.Net.WebExeception
If ex.Message.Contains("550") Then
Return False
Else
Throw
End If
This will only handle WebExceptions and all others are thrown normally.
'Howard

GeneralThe operation has timed out
Jon Lewis
11:17 14 Jan '10  
Ocassionally I get a time out message from a upload with the FTPclient ("The operation has timed out").

The files that are uploaded are small. The error occurs after about 1 minute 40 seconds after creating the ftp object.

I am told by the FTP site I am connecting to that my process connects but does not send anything (and the FTP site times out the connection after 3 hours).

Does anyone have any insight to this? Frown

Jon
GeneralRe: The operation has timed out
Howard Richards
8:17 19 Jan '10  
Only thing that comes to mind is the link between the two sites.

Try testing the operation using a local test FTP server on a PC you control. If this works then it might be related to firewalls between the two systems.

I have an issue with one client with FTP from Win7/Vista box via their VPN - the VPN dies for large uploads.

'Howard

QuestionHow to get Sub Dirs - which Function?
streezer
23:35 28 Dec '09  
Hey Howard,
i try to get the subdirs from the currentDir (.CurrentDirectory) on a double click - item event.

isnt the constructor
FTPdirectory(string dir, string path)
meant for that?
what do i have to give with in the var. "dir" and why a var. "path"? can you give me some example please?
or is there an other way to get all directorys and files from a selected / double clicked dir?

Thanks for your help,
streezer
AnswerRe: How to get Sub Dirs - which Function?
Howard Richards
0:47 31 Dec '09  
FtpDirectory is a class created by the FTPclient class when you call ListDirectoryDetail.

It returns a list of either files and/or directories

'Howard

GeneralProblem at the end of file download
Ivan.vv
2:23 26 Nov '09  
When I trying to download file from ftp "responseStream.Read" works Ok up to the last byte, but at the end of file, it stuck forever in "Read", when it supposed to return zero. I've modified the code a little, it counts received bytes and exits from the do{Read}while loop when all bytes received. Now it throws exception in "responseStream.Close()"
"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond".
My FTP server is a Microsoft's IIS 6.0, other FTP programs works Ok with it.
How can I solve this problem?
Thanks,
Ivan
GeneralThis is what I was looking for
mnsant
4:49 17 Nov '09  
Thanks alot for posting such a nice code, it was really big help for me.
GeneralThe remote server returned an error: (530) Not logged in.
PeterWashington
6:13 29 Oct '09  
Hi folks, I'm a bit of a newbie when it comes to FTP, so please excuse me if the answer to my question is utterly obvious to some of you.

I threw together the following code to try out Howards marvellous FTP Client :-

Public Sub FtpSplurgeTest()
Const FileName As String = "LITT09101314.csv"
'To use FtpClient, create a new instance of the object, defining the host, username and password.
Dim myFtp As New Utilities.FTP.FTPclient("ftp://" & FtpHostName, FtpUserName, FtpPassword)
'To get a directory listing of the FTP server's /pub directory:
Dim DirList As List(Of String) = myFtp.ListDirectory("/")
Dim fullList As Utilities.FTP.FTPdirectory = myFtp.ListDirectoryDetail("/")
fullList = myFtp.ListDirectoryDetail("/Test/")
'To determine which of these are files, use the GetFiles function:
Dim filesOnly As Utilities.FTP.FTPdirectory = fullList.GetFiles()
'To download or upload a file - a simple example:
myFtp.Download("/Test/" & FileName, ".\TestData\" & FileName)
myFtp.Upload(".\TestData\" & FileName, "/Test/" & FileName & "-1")
'Or a more complex example, downloading all the files from a directory.
For Each file As Utilities.FTP.FTPfileInfo In myFtp.ListDirectoryDetail("/Test/").GetFiles
myFtp.Download(file, ".\TestData\" & file.Filename)
Next file
End Sub

But I get the error mentioned in the Thread Subject on the first line that actually tries to get Directory information. The example function from the CodeProject article works fine and downloads a file from my server to my client PC.
Could this problem have anything to do with the KeepAlive ?
I'm using VB Express 2008 on XP and my server is FileZilla and is running on XP Embedded.

Thanks in advance for any help that you give me.
GeneralRe: The remote server returned an error: (530) Not logged in.
PeterWashington
0:05 30 Oct '09  
Hi once again folks,

I now have a good sized portion of well scambled egg on my face Cry

I had been printing out the parameters that I was using to log in to the FTP Server, both the set that worked from the first, long hand, example supplied by Howard against the set of data being used by my "Splurge" function and I couldn't see any difference, more fool me !!! I had accidentally substituted a '_' for a '-' in the User Name Mad

My apologies for wasting everybodies time, and let that be a lesson to us all, NEVER assume that anything is correct just because you've typed that string SOoooo many times before !

Please consider this thread closed due to being solved.
GeneralRe: The remote server returned an error: (530) Not logged in.
Howard Richards
0:53 30 Oct '09  
Hey no problem.. we've all been there

Howard

'Howard

GeneralRe: The remote server returned an error: (530) Not logged in.
PeterWashington
0:39 31 Oct '09  
Thank you for your magnanimous response Howard.

Yes we have all been there, (though we hate to admit it) Wink But not normally quite so publically !! Frown

Anyway, having got logged in, I managed to get all the functionality you provide working in very short order. It really is a very nice library, and very easy to use.

Thanks again, and kep up the good work.

Cheers Peter
Generalc#
sharmila03
20:04 11 Oct '09  
I received the below error while download more than one file from ftp using ftpwebrequest.
Error message: "The remote server returned an error: (500) Syntax error, command unrecognized."
GeneralThe remote server returned an error: (500) Syntax error, command unrecognized.
sharmila03
20:02 11 Oct '09  
I received the below error while download more than one file from ftp using ftpwebrequest.
Error message: "The remote server returned an error: (500) Syntax error, command unrecognized."
GeneralRe: The remote server returned an error: (500) Syntax error, command unrecognized.
Megha_CE
23:40 20 Jan '10  
try using ftp.UsePassive = False, i.e. set ftpwebrequest's request mode passive to false in the source code's ftpclient class. It solved same problem for me. refer this,
http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.usepassive.aspx
GeneralHow to add the Detailled List to a ListView?
streezer
3:41 5 Oct '09  
how you have to add the - "FTP.FTPdirectory" ListDirectoryDetail() - List to an ListView or Treeview?
GeneralRe: How to add the Detailled List to a ListView?
Howard Richards
3:59 5 Oct '09  
Try using a recursive search to return list items.. see this post

An FTP client library for .NET 2.0[^]

'Howard


Last Updated 20 Mar 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010