Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#
Article

LightFTPClient: a basic FTP client

Rate me:
Please Sign up or sign in to vote.
4.39/5 (11 votes)
9 May 2004CPOL1 min read 160K   2.3K   48   53
LightFTPClient is a basic FTP client.

Introduction

This is a simple FTP client written in C#.

Usage

LightFTPClient is a basic FTP client. To implement FTP communication, I adapted the FTP client library of Jaimon Mathew.

LightFTPClient maintains data (username, password, port) of your connections in a list, so you can select one directly from the list. These data will be persisted into a file named lightftpclient.bin. This file will be created in the same directory where the executable is located. Data is saved when the program is closed.

Sample screenshot

System and error messages are shown in the middle area. At the bottom, on the left, there is the local path and its contents, on the right there is the remote path and its contents.

The following function is invoked to populate list referring local path.

C#
protected void PopulateLocalFileList()
{
    //Populate listview with files
    string[] lvData =  new string[4];
    string sPath = txtLocalPath.Text;

    InitListView(lvLocalFiles);

    if (sPath.Length > 3)
        addParentDirectory(lvLocalFiles);
    else
    {

    }
    try
    {
        string[] stringDir = Directory.GetDirectories(sPath);
        string[] stringFiles = Directory.GetFiles(sPath);

        string stringFileName = "";
        DateTime dtCreateDate, dtModifyDate;
        Int64 lFileSize = 0;

        foreach (string stringFile in stringDir)
        {
            stringFileName = stringFile;
            FileInfo objFileSize = new FileInfo(stringFileName);
            lFileSize = 0;
            dtCreateDate = objFileSize.CreationTime;
                        dtModifyDate = objFileSize.LastWriteTime; 

            //create listview data
            lvData[0] = "";
            lvData[1] = GetPathName(stringFileName);
            lvData[2] = formatSize(lFileSize);
            lvData[3] = formatDate(dtModifyDate);

            //Create actual list item
            ListViewItem lvItem = new ListViewItem(lvData,0); // 0 = directory
            lvLocalFiles.Items.Add(lvItem);

        }

        foreach (string stringFile in stringFiles)
        {
            stringFileName = stringFile;
            FileInfo objFileSize = new FileInfo(stringFileName);
            lFileSize = objFileSize.Length;
            dtCreateDate = objFileSize.CreationTime; 
                        dtModifyDate = objFileSize.LastWriteTime; 

            //create listview data
            lvData[0] = "";
            lvData[1] = GetPathName(stringFileName);
            lvData[2] = formatSize(lFileSize);
            lvData[3] = formatDate(dtModifyDate);

            //Create actual list item
            ListViewItem lvItem = new ListViewItem(lvData,1); // 1 = file
            lvLocalFiles.Items.Add(lvItem);

        }

        // Loop through and size each column header
        // to fit the column header text.
        foreach (ColumnHeader ch in this.lvLocalFiles.Columns)
        {
            ch.Width = -2;
        }

    }
    catch (IOException)
    {
        MessageBox.Show("Error: Drive not ready or directory does not exist.");
    }
    catch (UnauthorizedAccessException)
    {
        MessageBox.Show("Error: Drive or directory access denided.");
    }
    catch (Exception ee)
    {
        MessageBox.Show("Error: " + ee);
    }
    lvLocalFiles.Invalidate();
    lvLocalFiles.Update();

}

Clicking on the header "Name", you can sort file list. To sort the file list, I make an implementation of the IComparer interface.

C#
public class ListViewColumnSorter : IComparer
{
    /// <SUMMARY>
    /// Specifies the column to be sorted
    /// </SUMMARY>
    private int ColumnToSort;
    /// <SUMMARY>
    /// Specifies the order in which to sort (i.e. 'Ascending').
    /// </SUMMARY>
    private SortOrder OrderOfSort;
    /// <SUMMARY>
    /// Case insensitive comparer object
    /// </SUMMARY>
    private CaseInsensitiveComparer ObjectCompare;

    /// <SUMMARY>
    /// Class constructor.  Initializes various elements
    /// </SUMMARY>
    public ListViewColumnSorter()
    {
        // Initialize the column to '1'
        ColumnToSort = 1;

        // Initialize the sort order to 'Ascending'
        OrderOfSort = SortOrder.Ascending;

        // Initialize the CaseInsensitiveComparer object
        ObjectCompare = new CaseInsensitiveComparer();
    }

    /// <SUMMARY>
    /// This method is inherited from the IComparer interface.  
    /// It compares the two objects passed using
    /// a case insensitive comparison.
    /// 
    /// Comparison rules: 
    /// - directories are listed before (ascending)
    ///           or after (descending) than files;
    /// - both directories both files are listed
    ///           in ascending or descending order.
    /// </SUMMARY>
    /// <PARAM name="x">First object to be compared</PARAM>
    /// <PARAM name="y">Second object to be compared</PARAM>
    /// <RETURNS>The result of the comparison. "0" if equal,
    /// negative if 'x' is less than 'y' and positive
    /// if 'x' is greater than 'y'</RETURNS>
    public int Compare(object x, object y)
    {
        int compareResult;
        ListViewItem listviewX, listviewY;

        // Cast the objects to be compared to ListViewItem objects
        listviewX = (ListViewItem)x;
        listviewY = (ListViewItem)y;

        if ( (listviewX.ImageIndex == listviewY.ImageIndex) 
          ||  ((listviewX.ImageIndex +listviewY.ImageIndex) == 2) )
        {
            // items are of the same type (2 directory or 2 files)
            // with a tirck (listviewX.ImageIndex +listviewY.ImageIndex)
            // one is a link, one is a dir
            // Compare the two items
            compareResult = 
              ObjectCompare.Compare(listviewX.SubItems[ColumnToSort].Text,
              listviewY.SubItems[ColumnToSort].Text);
        }
        else
        {
            // items are of different type
            // directory are listed before (ascending order) than files
            // or after (descending order) than files
            // Note: in my FTP client
            // ImageIndex = 0 means that item is a directory
            // ImageIndex = 1 means that item is a file
            // ImageIndex = 2 means that item is a link
            compareResult = 
              ((listviewX.ImageIndex < listviewY.ImageIndex)?-1:1);
        }

        // Calculate correct return value based on object comparison
        if (OrderOfSort == SortOrder.Ascending)
        {
            // Ascending sort is selected, return normal
            // result of compare operation
            return compareResult;
        }
        else if (OrderOfSort == SortOrder.Descending)
        {
            // Descending sort is selected,
            // return negative result of compare operation
            return (-compareResult);
        }
        else
        {
            // Return '0' to indicate they are equal
            return 0;
        }
    }
    
    /// <SUMMARY>
    /// Gets or sets the number of the column to which
    /// to apply the sorting operation (Defaults to '0').
    /// </SUMMARY>
    public int SortColumn
    {
        set
        {
            ColumnToSort = value;
        }
        get
        {
            return ColumnToSort;
        }
    }

    /// <SUMMARY>
    /// Gets or sets the order of sorting to apply
    /// (for example, 'Ascending' or 'Descending').
    /// </SUMMARY>
    public SortOrder Order
    {
        set
        {
            OrderOfSort = value;
        }
        get
        {
            return OrderOfSort;
        }
    }
}

And I assigned it to the ListView.

C#
.....
// Create an instance of a ListView column sorter and assign it 
// to ListView control.
lvwColumnSorter = new ListViewColumnSorter();
this.lvFiles.ListViewItemSorter = lvwColumnSorter;

lvwLocalColumnSorter = new ListViewColumnSorter();
this.lvLocalFiles.ListViewItemSorter = lvwLocalColumnSorter;
.....

To navigate, use mouse or type path in the respective input box, then type "Enter".

Use drag & drop to download or upload a file, and the context menu to rename/delete it or to refresh content (it's also possible to use function key).

Sample screenshot

System Requirements

This application was developed using VS.NET/C# and Microsoft .NET Framework 1.1, and tested on Win 2000.

License

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


Written By
Web Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to change the property of directory? Pin
wen22920218-Oct-07 17:46
wen22920218-Oct-07 17:46 
GeneralVery Nice! Pin
stevo346312-Sep-07 17:13
stevo346312-Sep-07 17:13 
QuestionDoes this support proxy? Pin
Sunil Kumar Sharma "Sun"27-Aug-06 21:28
Sunil Kumar Sharma "Sun"27-Aug-06 21:28 
GeneralPASV and PORT Pin
dongshan23-Aug-06 22:04
dongshan23-Aug-06 22:04 
AnswerRe: PASV and PORT [modified] Pin
Massimo Beatini1-Sep-06 4:53
Massimo Beatini1-Sep-06 4:53 
GeneralRe: PASV and PORT Pin
dongshan9-Oct-06 21:03
dongshan9-Oct-06 21:03 
GeneralRe: PASV and PORT Pin
maximus.dec.meridius2-Sep-09 9:47
maximus.dec.meridius2-Sep-09 9:47 
GeneralTimeouts in FtpClient.readLine() Pin
Simon Steed (SimonAntony)4-Apr-06 3:51
Simon Steed (SimonAntony)4-Apr-06 3:51 
AnswerRe: Timeouts in FtpClient.readLine() Pin
Massimo Beatini4-Apr-06 4:39
Massimo Beatini4-Apr-06 4:39 
GeneralRe: Timeouts in FtpClient.readLine() Pin
Simon Steed (SimonAntony)4-Apr-06 22:46
Simon Steed (SimonAntony)4-Apr-06 22:46 
Hi,

I managed to sort out the problem, it was indeed in the Download method but was not covered by your previous answer.

The original code:

			if ( cSocket.Connected ) cSocket.Close();<br />
<br />
            <br />
                this.readResponse();<br />
<br />
			if( this.resultCode != 226 && this.resultCode != 250 )<br />
				FireException(this.result.Substring(4));<br />
		}


And the fixed code:

			if ( cSocket.Connected ) cSocket.Close();<br />
<br />
            if(cSocket.Connected == true)<br />
                this.readResponse();<br />
<br />
			if( this.resultCode != 226 && this.resultCode != 250 )<br />
				FireException(this.result.Substring(4));<br />
		}


BAsically i'm checking to be sure the socket is still actually connected, if not then skip reading any more responses, otherwise carry on as normal. What appeared to be happening was the socket was being closed then you were reading more data from the socket hence the deadlock - this fix addresses the problem I experienced.

I'm surprised no one else has had the same problem though, really surprised!

Regards

Si Big Grin | :-D
AnswerRe: Timeouts in FtpClient.readLine() Pin
Massimo Beatini5-Apr-06 0:25
Massimo Beatini5-Apr-06 0:25 
GeneralRe: Timeouts in FtpClient.readLine() Pin
Member 361802521-Dec-06 7:08
Member 361802521-Dec-06 7:08 
GeneralRe: Timeouts in FtpClient.readLine() Pin
Member 1528196617-Jul-21 21:57
Member 1528196617-Jul-21 21:57 
GeneralRe: Timeouts in FtpClient.readLine() Pin
Member 1528196617-Jul-21 22:27
Member 1528196617-Jul-21 22:27 
QuestionAdditions? Pin
rball15-Mar-06 13:06
rball15-Mar-06 13:06 
AnswerRe: Additions? Pin
Massimo Beatini15-Mar-06 23:17
Massimo Beatini15-Mar-06 23:17 
GeneralRe: Additions? Pin
rball16-Mar-06 6:06
rball16-Mar-06 6:06 
GeneralError return : invalid response for PASV command Pin
seb.4928-Feb-06 4:33
seb.4928-Feb-06 4:33 
AnswerRe: Error return : invalid response for PASV command Pin
Massimo Beatini28-Feb-06 20:55
Massimo Beatini28-Feb-06 20:55 
GeneralRe: Error return : invalid response for PASV command Pin
seb.4928-Feb-06 23:01
seb.4928-Feb-06 23:01 
AnswerRe: Error return : invalid response for PASV command Pin
Massimo Beatini28-Feb-06 23:34
Massimo Beatini28-Feb-06 23:34 
GeneralRe: Error return : invalid response for PASV command Pin
seb.491-Mar-06 2:26
seb.491-Mar-06 2:26 
AnswerRe: Error return : invalid response for PASV command Pin
Massimo Beatini1-Mar-06 2:49
Massimo Beatini1-Mar-06 2:49 
GeneralRe: Error return : invalid response for PASV command Pin
seb.491-Mar-06 3:19
seb.491-Mar-06 3:19 
AnswerRe: Error return : invalid response for PASV command Pin
Massimo Beatini1-Mar-06 3:25
Massimo Beatini1-Mar-06 3:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.