Click here to Skip to main content
Click here to Skip to main content

LightFTPClient: a basic FTP client

By , 9 May 2004
 

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.

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.

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.

.....
// 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)

About the Author

Massimo Beatini
Web Developer
Italy Italy
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to change the property of directory?memberwen22920218 Oct '07 - 17:46 
GeneralVery Nice!memberstevo346312 Sep '07 - 17:13 
QuestionDoes this support proxy?memberSunil Kumar Sharma "Sun"27 Aug '06 - 21:28 
GeneralPASV and PORTmemberdongshan23 Aug '06 - 22:04 
AnswerRe: PASV and PORT [modified]memberMassimo Beatini1 Sep '06 - 4:53 
Hi,
LigthFTPClient was not built to use "active mode" (PORT mode), to use it I have to redesign the application logic (probably in the future I'll do it).
 
The following text explain how "active mode" works:

A client opens a connection to the FTP control port (port 21) of an FTP server. So that the server will be later able to send data back to the client machine, a second (data) connection must be opened between the server and the client.
 
To make this second connection, the client sends a PORT command to the server machine. This command includes parameters that tell the server which IP address to connect to and which port to open at that address - in most cases this is intended to be a high numbered port on the client machine.
 
The server then opens that connection, with the source of the connection being port 20 on the server and the destination being the port identified in the PORT command parameters.

 
About your code
dongshan wrote:
socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
ep = New IPEndPoint(Dns.Resolve(ipAddress).AddressList(0), port)
socket.Connect(ep)

 
To receive data from FTP server you must create a socket but you haven't to connect to it, the ftp server has to do.
You have to listen on it, you should change the code in this way (C# code, I hope it wasn't a problem)
....
socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
ep = new IPEndPoint(Dns.Resolve(ipAddress).AddressList[0], port);
// bind to the local endpoint
socket.Bind(ep);
socket.Listen(1000);
 
socket.BeginAccept(new AsyncCallback(this.AcceptCallback), socket);
...
 
/// 
/// Decription: Call back method to accept new connections.
/// 
/// <param name="ar">Status of an asynchronous operation.</param>
private void AcceptCallback(IAsyncResult ar)
{
        // Get the socket that handles the client request.
	Socket listener = (Socket) ar.AsyncState;
	Socket handler = listener.EndAccept(ar);
 
       // use handler to receive ftp server's data

        Byte[] receiveBuffer = new Byte[1024];
	string l_strOutput = "",l_strTemp = "";
 
	for ( ; ( l_iRetval = handler.Receive(receiveBuffer)) > 0 ;  ) 
	{
   	        l_strTemp = Encoding.ASCII.GetString(receiveBuffer,0,l_iRetval);
		l_strOutput += l_strTemp;
		if ( handler.Available == 0 )
		{
			break;
		}
	}
 

	handler.Close();
 
}
I hope this can help you.Smile | :)
 
Bye
Massimo
 

 

-- modified at 11:00 Friday 1st September, 2006
GeneralRe: PASV and PORTmemberdongshan9 Oct '06 - 21:03 
GeneralRe: PASV and PORTmembermaximus.dec.meridius2 Sep '09 - 9:47 
GeneralTimeouts in FtpClient.readLine()memberSimon Steed (MadSi)4 Apr '06 - 3:51 
AnswerRe: Timeouts in FtpClient.readLine()memberMassimo Beatini4 Apr '06 - 4:39 
GeneralRe: Timeouts in FtpClient.readLine()memberSimon Steed (MadSi)4 Apr '06 - 22:46 
AnswerRe: Timeouts in FtpClient.readLine()memberMassimo Beatini5 Apr '06 - 0:25 
GeneralRe: Timeouts in FtpClient.readLine()memberregistrationisformexicans21 Dec '06 - 7:08 
QuestionAdditions?memberrball15 Mar '06 - 13:06 
AnswerRe: Additions?memberMassimo Beatini15 Mar '06 - 23:17 
GeneralRe: Additions?memberrball16 Mar '06 - 6:06 
GeneralError return : invalid response for PASV commandmemberseb.4928 Feb '06 - 4:33 
AnswerRe: Error return : invalid response for PASV commandmemberMassimo Beatini28 Feb '06 - 20:55 
GeneralRe: Error return : invalid response for PASV commandmemberseb.4928 Feb '06 - 23:01 
AnswerRe: Error return : invalid response for PASV commandmemberMassimo Beatini28 Feb '06 - 23:34 
GeneralRe: Error return : invalid response for PASV commandmemberseb.491 Mar '06 - 2:26 
AnswerRe: Error return : invalid response for PASV commandmemberMassimo Beatini1 Mar '06 - 2:49 
GeneralRe: Error return : invalid response for PASV commandmemberseb.491 Mar '06 - 3:19 
AnswerRe: Error return : invalid response for PASV commandmemberMassimo Beatini1 Mar '06 - 3:25 
GeneralRe: Error return : invalid response for PASV commandmemberseb.491 Mar '06 - 3:37 
AnswerRe: Error return : invalid response for PASV commandmemberMassimo Beatini1 Mar '06 - 4:15 
GeneralRe: Error return : invalid response for PASV commandmemberseb.491 Mar '06 - 4:18 
AnswerRe: Error return : invalid response for PASV commandmemberMassimo Beatini1 Mar '06 - 5:01 
GeneralRe: Error return : invalid response for PASV commandmembermaximus.dec.meridius8 Sep '09 - 8:55 
GeneralBinary or Asciimemberjpazgier30 Aug '05 - 8:00 
AnswerRe: Binary or AsciisussMassimo Beatini31 Aug '05 - 1:18 
GeneralHelp Me Pleasemembercodeajay8 Mar '05 - 20:18 
Questionpassive or active ?memberGabbueno8 Mar '05 - 3:29 
AnswerRe: passive or active ?sussmbeatini8 Mar '05 - 3:51 
QuestionDo i need any license to use your code?sussIndian Ocean20 Feb '05 - 19:04 
AnswerRe: Do i need any license to use your code?sussmbeatini20 Feb '05 - 21:06 
Questionbroken pipe?memberyomomma121 Dec '04 - 12:11 
AnswerRe: broken pipe?sussmbeatini21 Dec '04 - 23:50 
GeneralRe: broken pipe?memberyomomma122 Dec '04 - 10:03 
AnswerRe: broken pipe?memberTom Willett8 Dec '05 - 9:05 
AnswerRe: broken pipe?memberMassimo Beatini10 Dec '05 - 13:11 
GeneralAnother free, open-source .NET FTP componentsussHans Andersen11 Nov '04 - 12:03 
Generalthread.sleep(700)memberGabbueno5 Nov '04 - 2:15 
GeneralRe: thread.sleep(700)membermbeatini5 Nov '04 - 3:12 
QuestionWrong warning message?memberShlomog30 Oct '04 - 0:51 
AnswerRe: Wrong warning message?sussmbeatini1 Nov '04 - 5:54 
Questionhow to add quota limits to a user using .net interfacemembersreedhar.prudhvi20 Jun '04 - 21:26 
Questionbug?memberwiseleyb2 Jun '04 - 4:33 
AnswerRe: bug?sussmbeatini8 Jun '04 - 5:09 
GeneralRe: bug?memberFrancisco Tovar28 Jan '05 - 6:24 
GeneralArticle SuggestionmemberAaron Eldreth10 May '04 - 10:57 

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 10 May 2004
Article Copyright 2004 by Massimo Beatini
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid