How to create an FTP client in C#

Creating an FTP client in a CLR compliant languge such as C# is truly a breeze compared to native languages like C or C++. The .NET Framework class library hands you the useful classes of System.Net on a platter which you can use to write your FTP functionality without worrying about things like memory allocation, protocol handling, etc. The .NET API does that on your behalf!

Before writing the code, it is useful to sit down and give a thought to your program design. Do you want to create a library to reuse in a larger project, or just a Windows app that acts as an FTP client? If you are a student doing some test project, I bet its the latter you will prefer! However, object-oriented programming and the concept of object-reuse dictate that you create a library first and then proceed with building your GUI, though it takes a little bit of extra effort.

In this tutorial I’ll briefly explain the functionality of libftp, an FTP library I have created in C# which I can seamlessly integrate into any application I develop or distribute it as a class-library/DLL. I also built a proof-of-concept Windows Forms application along with, to ensure the new functionality is working well. The library and application are available as open-source project on codeplex. You can browse through the code, understand it and straight away plug-it in your .NET application 100% free of cost.

The workings of this library is simplicity itself. There are just two classes – ftper class that acts as an interface to this library. It exposes public methods like connect(), disconnect(), browse(), addFilesToDownloadQueue(), etc. Here are some implementation details:

public bool isProcessing()
{
    return _threadRunning; //check if a thread is running for up/download
}

public List<ftpinfo> connect(string host, string username, string password)
{
    return ftpobject.connect(host,username,password);
}

public void disconnect()
{
    if (_threadRunning)
    {
        _threadRunning=false;
    }

    int timeout=60; //seconds
    DateTime start=DateTime.Now;
    while(queue.Count==0) //wait till running up/download threads complete.
    {
        if (DateTime.Now.Subtract(start).Seconds>timeout)
            break;
    }
}

public List<ftpinfo> browse(string path)
{
    return ftpobject.browse(path);
}

The second class, ftp is the core class which does the actual ftp stuff by connecting to the remote-host and transferring files. It is the underlying class for ftpobject in the above code.  It also exposes some events like uploadComplete and downloadComplete that our client can subscribe to, so that he/she can get notified of these events.


internal ftp() //hide constructor from the outside world.
{
context = SynchronizationContext.Current;
}

//tests connection and browse to home-directory
 public List<ftpinfo> connect(string host,string username,string password)
 {
 this._username=username;
 this._password=password;

//FtpWebRequest.Create(host); //test connect;
 context = SynchronizationContext.Current;

return browse(host);
 }

//public bool exists(string remotefilename)
 //{
 //    return true;
 //}

/// <summary>
 /// Fetch all files/folders in this directory and return the ftpinfo array.
 /// </summary>
 public List<ftpinfo> browse(string path) //eg: "ftp.xyz.org", "ftp.xyz.org/ftproot/etc"
 {
 FtpWebRequest request=(FtpWebRequest)FtpWebRequest.Create(path);
 request.Method=WebRequestMethods.Ftp.ListDirectoryDetails;
 List<ftpinfo> files=new List<ftpinfo>();

//request.Proxy = System.Net.WebProxy.GetDefaultProxy();
 //request.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
 request.Credentials = new NetworkCredential(_username, _password);
 Stream rs=(Stream)request.GetResponse().GetResponseStream();

OnStatusChange("CONNECTED: " + path, 0, 0);

StreamReader sr = new StreamReader(rs);
 string strList = sr.ReadToEnd();
 string[] lines=null;

if (strList.Contains("\r\n"))
 {
 lines=strList.Split(new string[] {"\r\n"},StringSplitOptions.None);
 }
 else if (strList.Contains("\n"))
 {
 lines=strList.Split(new string[] {"\n"},StringSplitOptions.None);
 }

//now decode this string array

if (lines==null || lines.Length == 0)
 return null;

foreach(string line in lines)
 {
 if (line.Length==0)
 continue;
 //parse line
 Match m= GetMatchingRegex(line);
 if (m==null) {
 //failed
 throw new ApplicationException("Unable to parse line: " + line);
 }

ftpinfo item=new ftpinfo();
 item.filename = m.Groups["name"].Value.Trim('\r');
 item.path = path;
 item.size = Convert.ToInt64(m.Groups["size"].Value);
 item.permission = m.Groups["permission"].Value;
 string _dir = m.Groups["dir"].Value;
 if(_dir.Length>0  && _dir != "-")
 {
 item.fileType = directoryEntryTypes.directory;
 }
 else
 {
 item.fileType = directoryEntryTypes.file;
 }

try
 {
 item.fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value);
 }
 catch
 {
 item.fileDateTime = DateTime.MinValue; //null;
 }

files.Add(item);
 }

return files;
 }

The reason I’ve kept the constructor method ftp() internal is that we don’t want our client creating these objects as they form the core ftp functionality that only our library is supposed to use. By using the keyword “internal”, C# compiler understands that we don’t want this consturctor to be visible outside our assembly.

Advertisements

Published by

Prahlad Yeri

I am a Freelance Web Developer and Blogger. I like sharing my knowledge and contribute to open source.

One thought on “How to create an FTP client in C#”

  1. What is “GetMatchingRegex(line)” ? How can one guess its implementation.. I would strongly claim that if you have just defined that function and left the remaining article completely… It could be better useful.. Other things are easily available.. And you missed the most important thing what could make your article really meaningful..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s