Click here to Skip to main content
15,886,026 members
Articles / Programming Languages / C#

Simple FTP library in C#

Rate me:
Please Sign up or sign in to vote.
3.93/5 (11 votes)
12 Mar 2012CPOL8 min read 115.3K   5.3K   72  
A complete walkthrough for FTP protocol in C#
This is an old version of the currently published article.

Introduction

This is my maiden article in CodeProject. Here I shall be illustrating the usage of FTP in C# with an easy to use library that I have coded. I have used the classes FtpWebRequest and FtpWebResponse from the namespace System.Net,  and they are sealed classes (cannot be inherited). 

I have currently implemented these functions of FTP:

  • listing files, listing directories  
  • uploading, downloading files  
  • creating/deleting directories and deleting files 

Internals of the T-FTP class

The ftpserver, ftpuser, ftppass are the properties which represent the server name, FTP user, and FTP password.

C#
private string fserver,fuser,fpass;

public string ftpserver
{
    get { return fserver; }
    set { fserver = value; }

}
public string ftpuser
{
    get { return fuser; }
    set { fuser = value; }

}
public string ftppass
{
    get { return fpass; }
    set { fpass = value; }
}

Remove files/directories and make new directories

We create the instance of FtpWebRequest class with the FTP URL for the particular file or directory. Eg., if the file is in ftp://2shar.servehttp.com/httpdocs/myfiles/ then the corresponding URL would be as follows in the code:

C#
FtpWebRequest reqFTP= (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://2shar.servehttp.com/httpdocs/myfiles/);

Next, reqFTP.Credentials property takes object of class NetworkCredential created with the constructor specifying the username and password as arguments (we can also specify domain name in the constructor of NetworkCredential).

C#
reqFTP.Credentials = new NetworkCredential(ftpuser, ftppass);

The KeepAlive property specifies whether the connection has to be left open after the corresponding process by default the value is true.

UseBinary property specifies the mode of FTP operation you can opt for either binary or ascii mode. Each has its scenario in cases where you concerned with mostly text files and documents with ascii format texts you can initialise the property to false while if you have to transfer files such as media or pictures or any zip files where binary streams of data are involved its better to opt for binary that is set property to true.

In case of file upload there is a ContentLength property that has to be set with the length of the file that you are uploading.

FileInfo class provides you with the way to do that FileInfo class has property Length (gives us the size of the file), Name (gives the name of the file), FullName (full path for the file).

Method property of FtpWebRequest gives us the purpose of the request( whether you want to create a directory delete a directory or a file).

C#
 reqFTP.Method = WebRequestMethods.Ftp.DeleteFile; 
reqFTP.Method =  WebRequestMethods.Ftp.MakeDirectory; 
reqFTP.Method = WebRequestMethods.Ftp.RemoveDirectory;

Now we get the response for the FtpWebRequest and place it in the FtpWebRespnse object the response.StatusDescription gives us the information about the status of the FTP request which can be passed to the caller any exceptions can be handled by using a try catch block and can return the exception to the caller.

C#
//ftp make directory
public bool ftpmakedir(string serverUri)
{
    try
    {
        FtpWebRequest request = (FtpWebRequest)WebRequest.Create(
          new Uri("ftp://" + ftpserver + "/" + serverUri));
        request.Credentials = new NetworkCredential(ftpuser, ftppass);

        request.KeepAlive = false;
        request.UseBinary = true;
        request.Method = WebRequestMethods.Ftp.MakeDirectory;

        FtpWebResponse response = (FtpWebResponse)request.GetResponse();

        response.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
} 

//FTP delete directory
public bool ftpremovedir(string serverUri)
{

    try
    {

        FtpWebRequest request = (FtpWebRequest)WebRequest.Create(
          new Uri("ftp://" + ftpserver + "/" + serverUri));
        request.Credentials = new NetworkCredential(ftpuser, ftppass);

        request.KeepAlive = false;
        request.UseBinary = true;
        request.Method = WebRequestMethods.Ftp.RemoveDirectory;

        FtpWebResponse response = (FtpWebResponse)request.GetResponse();

        response.Close();
        return true;
    }
    catch (Exception ex)
    {
        return false;
    }
}

File Upload and Download

When an user is downloading a file one has to create the instance of FtpWebRequest class with the FTP URL for the particular file (e.g., if the file is in ftp://2shar.servehttp.com/httpdocs/myfile.zip then the corresponding URL would be as follows in the code.

C#
FtpWebRequest reqFTP= 
(FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://2shar.servehttp.com/httpdocs/myfile.zip);

Next, reqFTP.Credentials property takes object of class NetworkCredential created with the constructor specifying the username and password as arguements (we can also specify domain name in the constructor of NetworkCredential).

C#
reqFTP.Credentials = new NetworkCredential(ftpuser, ftppass);

The KeepAlive property specifies whether the connection has to be left open after the corresponding process by default the value is true.

UseBinary property specifies the mode of FTP operation you can opt for either binary or ASCII mode. Each has its scenario in cases where you concerned with mostly text files and documents with ascii format texts you can initialise the property to false while if you have to transfer files such as media or pictures or any zip files where binary streams of data are involved its better to opt for binary that is set property to true.

In case of file upload there is a ContentLength property that has to be set with the length of the file that you are uploading.

The FileInfo class provides you with the way to do that FileInfo class has property Length (gives us the size of the file), Name (gives the name of the file), FullName (full path for the file).

Method property of FtpWebRequest gives us the purpose of the request (whether you want to upload, download or delete etc..)

C#
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;

When we are downloading we initialize a new instance of the FileStream class with the specified path, creation mode, and write permission.

C#
FileStream fs = new FileStream(downloadpath + fileInf.Name, FileMode.Create, FileAccess.Write);

We can get the size of the download file using the FtpWebRequest.Method by setting the property to  WebRequestMethods.Ftp.GetFileSize ftpsize() does the job in this case. The response to the request created would give the size of file.

By taking a size/100 as the size of the byte buffer which we can use to read from the response stream and will be helpful to show the status and time of download.

We check the download_file event to be not equal to null so that there wouldn't be any unwanted behavior at run time due to event not being initialised by the user. 

C#
long leng = ftpsize("ftp://" + ftpserver + fileInf.ToString());
byte[] buff = new byte[leng/100];
int contentLen;
contentLen = responseStream.Read(buff, 0, buff.Length);

long len1 = leng;
while (contentLen != 0)
{
    fs.Write(buff, 0, contentLen);
    contentLen = responseStream.Read(buff, 0, buff.Length);
    leng -= contentLen;
    if (leng > 0)
    {
        if (download_file != null)
        {
            download_file(len1, leng);
        }
    }
}

Then we can read from the response stream and write the bytes that are read into the filestream object.

C#
contentLen = responseStream.Read(buff, 0, buff.Length);
fs.Write(buff, 0, contentLen);

Once we have completely read from the response stream we will close the filestream object and return the status of the response object to inform the caller about the status of his request (i.e., whether the file has been downloaded or not).

C#
fs.Close();
return response.StatusDescription.ToString();

public delegate void display_upload(long size, long now);
public event display_upload upload_file;
public delegate void display_download(long size, long now);
public event display_download download_file;
//we have created the events of corresponding delegate type so that user can just subcribe
// to the event and display the result in the way they intend to. 

The events and delegates for the upload and download are given above, this approach is used to implement the subscriber-publisher design where the tftp.dll has published set of events namely upload_file  and download_file which corresponds to type of delegates display_upload and display_download.

Now a person using the DLL file can subscribe to the event by using a function of his own function which should also has to be of the delegate type display_download and display_upload.

These functions of the user will be called whenever the event is raised. If you look at the code of download you can notice that we are raising the event in ftpdownload function download_file(len1,len) after reading each set of bytes from the response stream doing so we are calling the subscriber function that the person using the dll has written.

The functions that handle the download (ftpdownload) and upload (ftpupload) are listed below.

C#
//ftp file upload function
public string ftpupload(string filename,string url)
{
    try
    {
        FileInfo fileInf = new FileInfo (filename);
        string uri = "ftp://" + ftpserver +"/"+ url + fileInf.Name;
        FtpWebRequest reqFTP;

        reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpserver + url + fileInf.Name));

        reqFTP.Credentials = new NetworkCredential(ftpuser, ftppass);

        reqFTP.KeepAlive = false;

        reqFTP.Method = WebRequestMethods.Ftp.UploadFile;

        reqFTP.UseBinary = true;

        reqFTP.ContentLength = fileInf.Length;

        int buffLength = 2048;
        byte[] buff = new byte[buffLength];
        int contentLen;

        FileStream fs = fileInf.OpenRead();

        Stream strm = reqFTP.GetRequestStream();

        contentLen = fs.Read(buff, 0, buffLength);
        int i = 0;
        long leng = fs.Length;
        while (contentLen != 0)
        {
            strm.Write(buff, 0, contentLen);
            contentLen = fs.Read(buff, 0, buffLength);
            leng -= 2048;
            if (leng > 0)
            {
                upload_file(fs.Length, leng);
                //the event is being triggered.
            }
        }

        strm.Close();
        fs.Close();
        FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();

        Stream responseStream = response.GetResponseStream();


        return response.StatusDescription.ToString();
        //return the status of operation
    }
    catch (Exception ex)
    {

        return ex.Message.ToString();
        //return any exception that has been encountered
    }
}

////ftpdownload function
public string ftpdownload(string fileurl,string downloadpath)
{
    try
    {
        FileInfo fileInf = new FileInfo(fileurl);
        string uri = "ftp://" + ftpserver + fileInf;
        FtpWebRequest reqFTP;

        reqFTP = (FtpWebRequest)FtpWebRequest.Create(
          new Uri("ftp://" + ftpserver  + fileInf));

        reqFTP.Credentials = new NetworkCredential(ftpuser, ftppass);

        reqFTP.UseBinary = true;
        reqFTP.KeepAlive = false;

        reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
        FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();

        Stream responseStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(responseStream);
     
        FileStream fs = new FileStream(downloadpath + fileInf.Name, 
                                       FileMode.Create, FileAccess.Write);
        byte[] buff = new byte[2048];
        int contentLen;
        contentLen = responseStream.Read(buff, 0, buff.Length);

        long leng = ftpsize("ftp://" + ftpserver + fileInf);
        long len1 = leng;
        while (contentLen != 0)
        {

            fs.Write(buff, 0, contentLen);
            contentLen = responseStream.Read(buff, 0, buff.Length);
            leng -= 2048;
            if (leng > 0)
            {
                download_file(len1, leng);
                //the event is being triggered.
            }
        }

        fs.Close();
        return response.StatusDescription.ToString();
        //return the status of operation
    }
    catch (Exception ex)
    {
        return ex.Message.ToString();
        //return any exception that has been encountered
    }
}

//this will give us the size of the object that can be used in the event to display the
//status bar
public long ftpsize(string url)
{
    FtpWebRequest reqSize = (FtpWebRequest)FtpWebRequest.Create(new Uri(url));
    reqSize.Credentials = new NetworkCredential(ftpuser, ftppass);
    reqSize.Method = WebRequestMethods.Ftp.GetFileSize;
    //the method property is set.
    reqSize.UseBinary = true;

    FtpWebResponse loginresponse = (FtpWebResponse)reqSize.GetResponse();
    FtpWebResponse respSize = (FtpWebResponse)reqSize.GetResponse();
    respSize = (FtpWebResponse)reqSize.GetResponse();
    long size = respSize.ContentLength;
    respSize.Close();
    return size;
}

File and directory List

I will explain the directory retrieval now. First, we have to create the instance of the FtpWebRequest class with the FTP URL for the particular fdirectory (e.g., if the directory is ftp://2shar.servehttp.com/httpdocs/, then the corresponding URL for the request would be as follows.

C#
FtpWebRequest reqFTP= 
  (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://2shar.servehttp.com/httpdocs/);

Next, reqFTP.Credentials property takes object of class NetworkCredential created with the constructor specifying the username and password as arguments (we can also specify domain name in the constructor of NetworkCredential).

C#
reqFTP.Credentials = new NetworkCredential(ftpuser, ftppass);

KeepAlive property specifies whether the connection has to be left open after the corresponding process by default the value is true.

UseBinary property specifies the mode of FTP operation you can opt for either binary or ASCII mode. Each has its scenario in cases where you concerned with mostly text files and documents with ascii format texts you can initialise the property to false while if you have to transfer files such as media or pictures or any zip files where binary streams of data are involved its better to opt for binary that is set property to true.

FileInfo class provides you with the way to do that FileInfo class has property Length (gives us the size of the file), Name (gives the name of the file), FullName (full path for the file).

Method property of FtpWebRequest gives us the purpose of the request( whether you want to upload, download or delete or list files or directory etc..)

We use  WebRequestMethods.Ftp.ListDirectoryDetails so that we can display the details of creation date and size associated with each files, if only the file name is to be displayed then we can use WebRequestMethods.Ftp.ListDirectory.

C#
ftpreq.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

Thereafter we get the response for the FTP request and store it in a FtpWebResponse object (response) and get the stream for the response object and read the stream with help of a StreamReader object.The code is given below.

C#
FtpWebResponse response = (FtpWebResponse)ftpreq.GetResponse();
Stream responseStream = response.GetResponseStream();
StreamReader reader = new StreamReader(responseStream);

We can carefully index out the directory name and directory creation date from the string read by the StreamReader class use methods of String class namely String.IndexOf(string value,int startindex) use String.subString(int startindex,int length).

C#
if (s.IndexOf("<DIR>", 0) != -1)
{ 
    
    string k = removewhite(s);
    //removes unwanted white spaces and replaces it with the seperator
    // of your choice
    dirdate = k.Substring(0, k.IndexOf("<DIR>", 0));
    dirname = k.Substring(k.IndexOf("<DIR>", 0) + 5, 
       k.Length - (k.IndexOf("<DIR>", 0) + 5));
}

Now we can get the status of operation by returning the status of response object.

C#
return response.StatusDescription.ToString(); 

The functions that handle the list directories (listds) and list files (listfs) are listed below.

C++
//
public string listfs(string url)
{
    listfile= new Dictionary<string, string>(100000);
    try
    {
        string uri = "ftp://" + ftpserver + url;
        FtpWebRequest ftpreq;

        ftpreq = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpserver + url));

        ftpreq.Credentials = new NetworkCredential(ftpuser, ftppass);

        ftpreq.KeepAlive = true;

        ftpreq.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

        ftpreq.UseBinary = true;

        FtpWebResponse response = (FtpWebResponse)ftpreq.GetResponse();

        Stream responseStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(responseStream);
        //Console.WriteLine(reader.ReadToEnd());

        //Console.WriteLine("Directory List Complete,
        //    status {0}", response.StatusDescription);
        string s;
        string dirname, dirdate;
        s = reader.ReadLine();
        while (s != null)
        {
            if (s.IndexOf("<DIR>", 0) != -1)
            {
                string k = removewhite(s);
                dirdate = k.Substring(0, k.IndexOf("<DIR>", 0));
                dirname = k.Substring(k.IndexOf("<DIR>", 0) + 5, 
                  k.Length - (k.IndexOf("<DIR>", 0) + 5));
            }
            else
            {
                string[] k = resolvefiles(s);
                listfile.Add(k[3].ToString(), (Int32.Parse(k[2].ToString()) / 1024).ToString());
                display_collect_files(k[3].ToString(), (Int32.Parse(k[2].ToString()) / 1024).ToString(), k[0] + "-" + k[1]);
                //Console.WriteLine("date of creation -" + k[0] + "-" + k[1] + 
                //  " |size of file " + (Int32.Parse(k[2].ToString()) / 1024) + "kb |name of file =" + k[3].ToString());
            }
            s = reader.ReadLine();

        }
       
        reader.Close();
        response.Close();
        return "ok";
    }
    catch (Exception notok)
    {
        
        return notok.Message.ToString();
    }
}
public string listds( string url)
{
    listdir = new Dictionary<string, string>(100000);
    try
    {
        string uri = ftpserver + url;
        FtpWebRequest ftpreq;

        ftpreq = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpserver + url));

        ftpreq.Credentials = new NetworkCredential(ftpuser, ftppass);

        ftpreq.KeepAlive = true;

        ftpreq.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

        ftpreq.UseBinary = true;
        FtpWebResponse response = (FtpWebResponse)ftpreq.GetResponse();

        Stream responseStream = response.GetResponseStream();
        StreamReader reader = new StreamReader(responseStream);
        //Console.WriteLine(reader.ReadToEnd());

        //Console.WriteLine("Directory List Complete, 
        //    status {0}", response.StatusDescription);
        string s;
        string dirname, dirdate;
        s = reader.ReadLine();
        while (s != null)
        {
            if (s.IndexOf("<DIR>", 0) != -1)
            {
                string k = removewhite(s);
                dirdate = k.Substring(0, k.IndexOf("<DIR>", 0));
                dirname = k.Substring(k.IndexOf("<DIR>", 0) + 5, 
                                k.Length - (k.IndexOf("<DIR>", 0) + 5));
                listdir.Add(dirname, dirdate);
                display_collect_dirs(dirname, dirdate);

            }
            else
            {
                //string[] k = resolvefiles(s);
                //listfiles.Add(k[3].ToString(), (Int32.Parse(k[2].ToString()) / 1024).ToString());
                //Console.WriteLine("date of creation -" + k[0] + "-" + k[1] + 
                // " |size of file " + (Int32.Parse(k[2].ToString()) / 1024) +
                // "kb |name of file =" + k[3].ToString());

            }
            s = reader.ReadLine();

        }
      
        reader.Close();
        response.Close();
        return "ok";
    }
    catch (Exception notok)
    {
        
        return notok.Message.ToString();
    }
}

public  string removewhite(string k)
{
    string result = string.Empty;
    char[] a = k.ToCharArray();
    foreach (char a1 in a)
    {
        if (a1 != ' ')
        {
            result = result + a1.ToString();
        }
    }
    return result;
}
public  string[] resolvefiles(string files)
{
    string result = string.Empty;
    char[] a = files.ToCharArray();
    bool flags = false;
    foreach (char a1 in a)
    {
        if (a1 == ' ')
        {
            if (flags == false)
            {
                result = result + "#";
            }
            flags = true;
        }
        else
        {
            result = result + a1;
            flags = false;
        }
    }

    char[] seps = { '#' };
    string[] rets = result.Split(seps);
    return rets;

}
//

Using T-FTP Library in your project  

In the introduction tftp library interiors and working has been demonstrated now I shall explain on how you can hook this up with your project.

Properties

property name semantics datatype 
ftpuser represents the ftp username  string 
ftpserver represents ftp address string 
ftppass represents ftp user password string

Example

C#
tftp.tftp t = new tftp.tftp();
t.ftpuser = "ftpusername";
t.ftpserver = "websitename.com";
t.ftppass = "ftppassword";

1. Upload (with the status of upload)

If uploadme is the name of your function that is subscribing to the event upload_file.

Variables a and b represents total size in bytes and size yet to be downloaded/uploaded for the file.

C++
//
static void uploadme(long a, long b)
{
    Console.WriteLine(b + " out of " + a +"has been left to downloaded so far");
}

t.upload_file += new tftp.tftp.display_upload(uploadme);
//hooking up the event 'upload_file' with your 'upload_me' function of delegate type 'display_upload'
Console.WriteLine( t.ftpupload("e:\\splits.zip","/httpdocs/"));
//calling the ftpupload function with parameters path of the file,the url on server where it has to be uploaded
//

Similarly for the download function.

2. Listing directories

If listdir is the name of your function that is subscribing to the event display_collect_dirs.

listds is the function in the DLL that gives you the list of the directories ,the parameters of the function represent name of the directory and creation date of the directory.

C#
//
static void listdir(string a,string b)
{
    Console.WriteLine("directory name= "+a+" directory creation date= "+b);
}

t.display_collect_dirs += new tftp.tftp.collect_dirs(listdir);
//hooking up the event 'display_collect_dirs' with your
//'listdir' function of type delegate  'collect_dirs'
  
t.listds("/httpdocs/");
//listing the directories in url ftp://website.com/httpdocs
//

Similarly for listing files, we can use the listfs() function.

3. Listing files

If listfiles is the name of your function that is subscribing to the event display_collect_files.

listfs is the function in the DLL that gives you the list of the files, the parameters of the function represent name of the file, the size of the file and creation date of the file.

C#
//
static void listfiles(string a,string b,string c)
{
    Console.WriteLine("file name= "+a+" file size= "+b+" file creation date "+c);
}

t.display_collect_files += new tftp.tftp.collect_files(listdir);
//hooking up the event 'display_collect_files' with
//your 'listfiles' function of type delegate  'collect_files'

t.listfs("/httpdocs/");
//listing the files in url ftp://website.com/httpdocs

4. Make directory and delete directory and delete files

These functions have string return types which informs about the status of the operation. ftpmakedir, ftpremovedir, ftpdelete represents the three functions for making directory, deleting directory, and deleting file.

C#
//
bool flgs = t.ftpmakedir("httpdocs/tftp");
bool rem = t.ftpremovedir("httpdocs/tftp");
bool flags=t.ftpdelete("httpdocs/splits.zip"); 
//

Points of interest   

Thanks for reading through, you can learn more about the FTPWebRequest class in MSDN and FTPWebResponse class in MSDN.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer slswolution
India India
I enjoy coding , i have 1 year experience in .net and php, i am comparatively new hope to learn a alot as the time proceeds.

Comments and Discussions

Discussions on this specific version of this article. Add your comments on how to improve this article here. These comments will not be visible on the final published version of this article.