csharp: FTP Client Library using System.Net.FtpWebRequest

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Collections;
using Microsoft.VisualBasic;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows.Forms;
//https://www.codeproject.com/Articles/56321/A-Windows-FTP-Application
//https://www.codeproject.com/Articles/4472/Enhanced-BrowseForFolder-styled-TreeView
//https://github.com/ChrisRichner/TreeViewFolderBrowser
//https://www.codeproject.com/Articles/11991/An-FTP-client-library-for-NET
//https://github.com/aybe/Windows-API-Code-Pack-1.1
//https://github.com/dbarros/WindowsAPICodePack

namespace FTPLibrary
{
    #region "FTP client class"
    /// <summary>
    /// A wrapper class for .NET 2.0 FTP
    /// </summary>
    /// <remarks>
    /// This class does not hold open an FTP connection but
    /// instead is stateless: for each FTP request it
    /// connects, performs the request and disconnects.
    /// </remarks>
    public class FTPclient
    {
        #region Delegated & Events
        //Download Progress Changed Event
        public delegate void DownloadProgressChangedHandler(object sender, DownloadProgressChangedArgs e);
        public event DownloadProgressChangedHandler OnDownloadProgressChanged;

        //Download Completed Event
        public delegate void DownloadCompletedHandler(object sender, DownloadCompletedArgs e);
        public event DownloadCompletedHandler OnDownloadCompleted;

        //New Server Message Event
        public delegate void NewMessageHandler(object sender, NewMessageEventArgs e);
        public event NewMessageHandler OnNewMessageReceived;

        //Upload Progress Changed Event
        //Download Progress Changed Event
        public delegate void UploadProgressChangedHandler(object sender, UploadProgressChangedArgs e);
        public event UploadProgressChangedHandler OnUploadProgressChanged;

        //Upload Completed Event
        public delegate void UploadCompletedHandler(object sender, UploadCompletedArgs e);
        public event UploadCompletedHandler OnUploadCompleted;
        #endregion

        #region "CONSTRUCTORS"
        /// <summary>
        /// Blank constructor
        /// </summary>
        /// <remarks>Hostname, username and password must be set manually</remarks>
        public FTPclient()
        {
        }

        /// <summary>
        /// Constructor just taking the hostname
        /// </summary>
        /// <param name="Hostname">in either ftp://ftp.host.com or ftp.host.com form</param>
        /// <remarks></remarks>
        public FTPclient(string Hostname)
        {
            _hostname = Hostname;
        }

        /// <summary>
        /// Constructor taking hostname, username and password
        /// </summary>
        /// <param name="Hostname">in either ftp://ftp.host.com or ftp.host.com form</param>
        /// <param name="Username">Leave blank to use 'anonymous' but set password to your email</param>
        /// <param name="Password"></param>
        /// <remarks></remarks>
        public FTPclient(string Hostname, string Username, string Password)
        {
            _hostname = Hostname;
            _username = Username;
            _password = Password;
        }
        #endregion

        #region "Directory functions"
        /// <summary>
        /// Return a simple directory listing
        /// </summary>
        /// <param name="directory">Directory to list, e.g. /pub</param>
        /// <returns>A list of filenames and directories as a List(of String)</returns>
        /// <remarks>For a detailed directory listing, use ListDirectoryDetail</remarks>
        public List<string> ListDirectory(string directory)
        {
            //return a simple list of filenames in directory
            System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory));
            //Set request to do simple list
            ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectory;
            //Give Message of Command
            NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "List Directory", "NLST");
            OnNewMessageReceived(this, e);

            string str = GetStringResponse(ftp);
            //replace CRLF to CR, remove last instance
            str = str.Replace("\r\n", "\r").TrimEnd('\r');
            //split the string into a list
            List<string> result = new List<string>();
            result.AddRange(str.Split('\r'));
            return result;
        }

        /// <summary>
        /// Return a detailed directory listing
        /// </summary>
        /// <param name="directory">Directory to list, e.g. /pub/etc</param>
        /// <returns>An FTPDirectory object</returns>
        public FTPdirectory ListDirectoryDetail(string directory)
        {
            System.Net.FtpWebRequest ftp = GetRequest(GetDirectory(directory));
            //Set request to do simple list
            ftp.Method = System.Net.WebRequestMethods.Ftp.ListDirectoryDetails;
            ftp.UseBinary = true;
            //Give Message of Command
            NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "List Directory Details", "LIST");
            OnNewMessageReceived(this, e);
            string str = GetStringResponse(ftp);
            //replace CRLF to CR, remove last instance
            str = str.Replace("\r\n", "\r").TrimEnd('\r');
            //split the string into a list
            return new FTPdirectory(str, _lastDirectory);
        }

        #endregion

        #region "Upload: File transfer TO ftp server"
        /// <summary>
        /// Copy a local file to the FTP server
        /// </summary>
        /// <param name="localFilename">Full path of the local file</param>
        /// <param name="targetFilename">Target filename, if required</param>
        /// <returns></returns>
        /// <remarks>If the target filename is blank, the source filename is used
        /// (assumes current directory). Otherwise use a filename to specify a name
        /// or a full path and filename if required.</remarks>
        public bool Upload(string localFilename, string targetFilename)
        {
            //1. check source
            if (!File.Exists(localFilename))
            {
                throw (new ApplicationException("File " + localFilename + " not found"));
            }
            //copy to FI
            FileInfo fi = new FileInfo(localFilename);
            return Upload(fi, targetFilename);
        }

        #region Upload Variables
        System.Net.FtpWebRequest UploadFTPRequest = null;
        FileStream UploadFileStream = null;
        Stream UploadStream = null;
        bool UploadCanceled = false;
        FileInfo UploadFileInfo = null;
        #endregion

        /// <summary>
        /// Upload a local file to the FTP server
        /// </summary>
        /// <param name="fi">Source file</param>
        /// <param name="targetFilename">Target filename (optional)</param>
        /// <returns></returns>
        public bool Upload(FileInfo fi, string targetFilename)
        {
            //copy the file specified to target file: target file can be full path or just filename (uses current dir)

            //1. check target
            string target;
            if (targetFilename.Trim() == "")
            {
                //Blank target: use source filename & current dir
                target = this.CurrentDirectory + fi.Name;
            }
            else if (targetFilename.Contains("/"))
            {
                //If contains / treat as a full path
                target = AdjustDir(targetFilename);
            }
            else
            {
                //otherwise treat as filename only, use current directory
                target = CurrentDirectory + targetFilename;
            }

            string URI = Hostname + target;
            //perform copy
            UploadFTPRequest = GetRequest(URI);

            //Set request to upload a file in binary
            UploadFTPRequest.Method = System.Net.WebRequestMethods.Ftp.UploadFile;
            UploadFTPRequest.UseBinary = true;
            //Notify FTP of the expected size
            UploadFTPRequest.ContentLength = fi.Length;
            UploadFileInfo = fi;

            //create byte array to store: ensure at least 1 byte!
            const int BufferSize = 2048;
            byte[] content = new byte[BufferSize - 1 + 1];
            int dataRead;

            //open file for reading
            using (UploadFileStream = fi.OpenRead())
            {
                try
                {
                    //open request to send
                    using (UploadStream = UploadFTPRequest.GetRequestStream())
                    {
                        //Give Message of Command
                        NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Upload File", "STOR");
                        OnNewMessageReceived(this, e);

                        //Get File Size
                        Int64 TotalBytesUploaded = 0;
                        Int64 FileSize = fi.Length;
                        do
                        {
                            if (UploadCanceled)
                            {
                                NewMessageEventArgs CancelMessage = new NewMessageEventArgs("RESPONSE", "Upload Canceled.", "CANCEL");
                                OnNewMessageReceived(this, CancelMessage);
                                UploadCanceled = false;
                                return false;
                            }

                            dataRead = UploadFileStream.Read(content, 0, BufferSize);
                            UploadStream.Write(content, 0, dataRead);
                            TotalBytesUploaded += dataRead;
                            //Declare Event
                            UploadProgressChangedArgs DownloadProgress = new UploadProgressChangedArgs(TotalBytesUploaded, FileSize);

                            //Progress changed, Raise the event.
                            OnUploadProgressChanged(this, DownloadProgress);

                            System.Windows.Forms.Application.DoEvents();
                        } while (!(dataRead < BufferSize));

                        //Get Message and Raise Event
                        NewMessageEventArgs UPloadResponse = new NewMessageEventArgs("RESPONSE", "File Uploaded!", "STOR");
                        OnNewMessageReceived(this, UPloadResponse);

                        //Declare Event
                        UploadCompletedArgs Args = new UploadCompletedArgs("Successful", true);
                        //Raise Event
                        OnUploadCompleted(this, Args);

                        UploadStream.Close();
                    }

                }
                catch (Exception ex)
                {
                    //Declare Event
                    UploadCompletedArgs Args = new UploadCompletedArgs("Error: " + ex.Message, false);
                    //Raise Event
                    OnUploadCompleted(this, Args);
                }
                finally
                {
                    //ensure file closed
                    UploadFileStream.Close();
                }

            }


            UploadFTPRequest = null;
            return true;

        }

        public void CancelUpload(string UploadFileName)
        {
            if (UploadFileStream != null)
            {
                UploadFileStream.Close();
                UploadFTPRequest.Abort();
                //UploadFileInfo.Delete();
                UploadCanceled = true;
                UploadFTPRequest = null;
                this.FtpDelete(UploadFileName);
                MessageBox.Show("Upload Canceled");
            }
        }
        #endregion

        #region "Download: File transfer FROM ftp server"
        /// <summary>
        /// Copy a file from FTP server to local
        /// </summary>
        /// <param name="sourceFilename">Target filename, if required</param>
        /// <param name="localFilename">Full path of the local file</param>
        /// <returns></returns>
        /// <remarks>Target can be blank (use same filename), or just a filename
        /// (assumes current directory) or a full path and filename</remarks>
        public bool Download(string sourceFilename, string localFilename, bool PermitOverwrite)
        {
            //2. determine target file
            FileInfo fi = new FileInfo(localFilename);
            return this.Download(sourceFilename, fi, PermitOverwrite);
        }

        //Version taking an FtpFileInfo
        public bool Download(FTPfileInfo file, string localFilename, bool PermitOverwrite)
        {
            return this.Download(file.FullName, localFilename, PermitOverwrite);
        }

        //Another version taking FtpFileInfo and FileInfo
        public bool Download(FTPfileInfo file, FileInfo localFI, bool PermitOverwrite)
        {
            return this.Download(file.FullName, localFI, PermitOverwrite);
        }

        #region Download Variables
        System.Net.FtpWebRequest DownloadFTPRequest = null;
        FtpWebResponse DownloadResponse = null;
        Stream DownloadResponseStream = null;   //中文亂碼
        FileStream DownloadFileStream = null;
        FileInfo TargetFileInfo = null;
        bool DownloadCanceled = false;
        #endregion
        //Version taking string/FileInfo
        public bool Download(string sourceFilename, FileInfo targetFI, bool PermitOverwrite)
        {
            //1. check target
            if (targetFI.Exists && !(PermitOverwrite))
            {
                throw (new ApplicationException("Target file already exists"));
            }

            //2. check source
            string target;
            if (sourceFilename.Trim() == "")
            {
                throw (new ApplicationException("File not specified"));
            }
            else if (sourceFilename.Contains("/"))
            {
                //treat as a full path
                target = AdjustDir(sourceFilename);
            }
            else
            {
                //treat as filename only, use current directory
                target = CurrentDirectory + sourceFilename;
            }

            string URI = Hostname + target;

            //3. perform copy
            DownloadFTPRequest = GetRequest(URI);

            //Set request to download a file in binary mode
            DownloadFTPRequest.Method = System.Net.WebRequestMethods.Ftp.DownloadFile;
            DownloadFTPRequest.UseBinary = true;
            TargetFileInfo = targetFI;
            //open request and get response stream
            using (DownloadResponse = (FtpWebResponse)DownloadFTPRequest.GetResponse())
            {
                using (DownloadResponseStream = DownloadResponse.GetResponseStream())
                {
                    //System.Security.AccessControl.FileSecurity fileSecurity = new System.Security.AccessControl.FileSecurity(targetFI.FullName, System.Security.AccessControl.AccessControlSections.All);
                    //targetFI.SetAccessControl(fileSecurity);
                    //loop to read & write to file
                    using (DownloadFileStream = new FileStream(targetFI.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite))
                    {
                        try
                        {
                            //Give Message of Command
                            NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Download File", "RETR");
                            OnNewMessageReceived(this, e);
                            byte[] buffer = new byte[2048];
                            int read = 0;
                            Int64 TotalBytesRead = 0;
                            Int64 FileSize = this.GetFileSize(sourceFilename);
                            DownloadCanceled = false;
                            do
                            {
                                if (DownloadCanceled)
                                {
                                    NewMessageEventArgs CancelMessage = new NewMessageEventArgs("RESPONSE", "Download Canceled.", "CANCEL");

                                    DownloadCanceled = false;
                                    OnNewMessageReceived(this, CancelMessage);
                                    return false;
                                }

                                read = DownloadResponseStream.Read(buffer, 0, buffer.Length);
                                DownloadFileStream.Write(buffer, 0, read);
                                TotalBytesRead += read;
                                //Declare Event
                                DownloadProgressChangedArgs DownloadProgress = new DownloadProgressChangedArgs(TotalBytesRead, FileSize);

                                //Progress changed, Raise the event.
                                OnDownloadProgressChanged(this, DownloadProgress);

                                System.Windows.Forms.Application.DoEvents();

                            } while (!(read == 0));


                            //Get Message and Raise Event
                            NewMessageEventArgs NewMessageArgs = new NewMessageEventArgs("RESPONSE", DownloadResponse.StatusDescription, DownloadResponse.StatusCode.ToString());
                            OnNewMessageReceived(this, NewMessageArgs);

                            //Declare Event
                            DownloadCompletedArgs Args = new DownloadCompletedArgs("Successful", true);
                            //Raise Event
                            OnDownloadCompleted(this, Args);

                            DownloadResponseStream.Close();
                            DownloadFileStream.Flush();
                            DownloadFileStream.Close();
                            DownloadFileStream = null;
                            DownloadResponseStream = null;
                        }
                        catch (Exception ex)
                        {
                            //catch error and delete file only partially downloaded
                            DownloadFileStream.Close();
                            //delete target file as it's incomplete
                            targetFI.Delete();

                            //Decalre Event for Error
                            DownloadCompletedArgs DownloadCompleted = new DownloadCompletedArgs("Error: " + ex.Message, false);
                            //Raise Event
                            OnDownloadCompleted(this, DownloadCompleted);
                        }
                    }
                    if (DownloadFileStream != null)
                        DownloadResponseStream.Close();
                }
                if (DownloadFileStream != null)
                    DownloadResponse.Close();
            }
            return true;
        }

        public void CancelDownload()
        {
            if (DownloadFileStream != null)
            {
                DownloadFileStream.Close();

                DownloadFTPRequest.Abort();

                DownloadResponse.Close();
                DownloadResponseStream.Close();
                //DownloadFileStream = null;
                //DownloadResponseStream = null;
                TargetFileInfo.Delete();
                DownloadCanceled = true;
                MessageBox.Show("Download Canceled");
            }
        }
        #endregion

        #region "Other functions: Delete rename etc."
        /// <summary>
        /// Delete remote file
        /// </summary>
        /// <param name="filename">filename or full path</param>
        /// <returns></returns>
        /// <remarks></remarks>
        public bool FtpDelete(string filename)
        {
            //Determine if file or full path
            string URI = this.Hostname + GetFullPath(filename);

            System.Net.FtpWebRequest ftp = GetRequest(URI);
            //Set request to delete
            ftp.Method = System.Net.WebRequestMethods.Ftp.DeleteFile;
            try
            {
                //get response but ignore it
                string str = GetStringResponse(ftp);
                //Give Message of Command
                NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Delete File", "DELE");
                OnNewMessageReceived(this, e);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Determine if file exists on remote FTP site
        /// </summary>
        /// <param name="filename">Filename (for current dir) or full path</param>
        /// <returns></returns>
        /// <remarks>Note this only works for files</remarks>
        public bool FtpFileExists(string filename)
        {
            //Try to obtain filesize: if we get error msg containing "550"
            //the file does not exist
            try
            {
                long size = GetFileSize(filename);
                return true;

            }
            catch (Exception ex)
            {
                //only handle expected not-found exception
                if (ex is System.Net.WebException)
                {
                    //file does not exist/no rights error = 550
                    if (ex.Message.Contains("550"))
                    {
                        //clear
                        return false;
                    }
                    else
                    {
                        System.Windows.Forms.MessageBox.Show(ex.Message);
                        return false;
                    }
                }
                else
                {
                    System.Windows.Forms.MessageBox.Show(ex.Message);
                    return false;
                }
            }
        }

        /// <summary>
        /// Determine size of remote file
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        /// <remarks>Throws an exception if file does not exist</remarks>
        public long GetFileSize(string filename)
        {
            string path;
            if (filename.Contains("/"))
            {
                path = AdjustDir(filename);
            }
            else
            {
                path = this.CurrentDirectory + filename;
            }
            string URI = this.Hostname + path;
            System.Net.FtpWebRequest ftp = GetRequest(URI);
            //Try to get info on file/dir?
            ftp.Method = System.Net.WebRequestMethods.Ftp.GetFileSize;
            string tmp = this.GetStringResponse(ftp);
            //Give Message of Command
            NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Get File Size", "SIZE");
            OnNewMessageReceived(this, e);
            return GetSize(ftp);
        }

        public bool FtpRename(string sourceFilename, string newName)
        {
            //Does file exist?
            string source = GetFullPath(sourceFilename);
            if (!FtpFileExists(source))
            {
                throw (new FileNotFoundException("File " + source + " not found"));
            }

            //build target name, ensure it does not exist
            string target = GetFullPath(newName);
            if (target == source)
            {
                throw (new ApplicationException("Source and target are the same"));
            }
            else if (FtpFileExists(target))
            {
                throw (new ApplicationException("Target file " + target + " already exists"));
            }

            //perform rename
            string URI = this.Hostname + source;

            System.Net.FtpWebRequest ftp = GetRequest(URI);
            //Set request to delete
            ftp.Method = System.Net.WebRequestMethods.Ftp.Rename;
            ftp.RenameTo = target;
            ftp.UseBinary = true;

            try
            {
                //get response but ignore it
                string str = GetStringResponse(ftp);
                //Give Message of Command
                NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "File Rename", "RENAME");
                OnNewMessageReceived(this, e);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public bool FtpCreateDirectory(string dirpath)
        {
            //perform create
            string URI = this.Hostname + AdjustDir(dirpath);
            System.Net.FtpWebRequest ftp = GetRequest(URI);
            //Set request to MkDir
            ftp.Method = System.Net.WebRequestMethods.Ftp.MakeDirectory;
            try
            {
                //get response but ignore it
                string str = GetStringResponse(ftp);
                //Give Message of Command
                NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Make Directory", "MKD");
                OnNewMessageReceived(this, e);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }

        public bool FtpDeleteDirectory(string dirpath)
        {
            //perform remove
            string URI = this.Hostname + AdjustDir(dirpath);
            System.Net.FtpWebRequest ftp = GetRequest(URI);
            ftp.UseBinary = true;
            //Set request to RmDir
            ftp.Method = System.Net.WebRequestMethods.Ftp.RemoveDirectory;
            try
            {
                //get response but ignore it
                string str = GetStringResponse(ftp);
                //Give Message of Command
                NewMessageEventArgs e = new NewMessageEventArgs("COMMAND", "Remove Directory", "RMD");
                OnNewMessageReceived(this, e);
            }
            catch (Exception)
            {
                return false;
            }
            return true;
        }
        #endregion

        #region "private supporting fns"
        //Get the basic FtpWebRequest object with the
        //common settings and security
        private FtpWebRequest GetRequest(string URI)
        {
            //create request
            FtpWebRequest result = (FtpWebRequest)FtpWebRequest.Create(URI);
            //Set the login details
            result.Credentials = GetCredentials();
            //Do not keep alive (stateless mode)
            result.KeepAlive = false;
            result.UseBinary = true;
            return result;
        }


        /// <summary>
        /// Get the credentials from username/password
        /// </summary>
        private System.Net.ICredentials GetCredentials()
        {
            return new System.Net.NetworkCredential(Username, Password);
        }

        /// <summary>
        /// returns a full path using CurrentDirectory for a relative file reference
        /// </summary>
        private string GetFullPath(string file)
        {
            if (file.Contains("/"))
            {
                return AdjustDir(file);
            }
            else
            {
                return this.CurrentDirectory + file;
            }
        }

        /// <summary>
        /// Amend an FTP path so that it always starts with /
        /// </summary>
        /// <param name="path">Path to adjust</param>
        /// <returns></returns>
        /// <remarks></remarks>
        private string AdjustDir(string path)
        {
            return ((path.StartsWith("/")) ? "" : "/").ToString() + path;
        }

        private string GetDirectory(string directory)
        {
            string URI;
            if (directory == "")
            {
                //build from current
                URI = Hostname + this.CurrentDirectory;
                _lastDirectory = this.CurrentDirectory;
            }
            else
            {
                if (!directory.StartsWith("/"))
                {
                    throw (new ApplicationException("Directory should start with /"));
                }
                URI = this.Hostname + directory;
                _lastDirectory = directory;
            }
            return URI;
        }

        //stores last retrieved/set directory
        private string _lastDirectory = "";

        /// <summary>
        /// Obtains a response stream as a string
        /// </summary>
        /// <param name="ftp">current FTP request</param>
        /// <returns>String containing response</returns>
        /// <remarks>FTP servers typically return strings with CR and
        /// not CRLF. Use respons.Replace(vbCR, vbCRLF) to convert
        /// to an MSDOS string</remarks>
        private string GetStringResponse(FtpWebRequest ftp)
        {
            //Get the result, streaming to a string
            string result = "";
            using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
            {
                long size = response.ContentLength;
                using (Stream datastream = response.GetResponseStream())
                {
                    // 一中文亂碼問題 涂聚文 20180801
                    using (StreamReader sr = new StreamReader(datastream, System.Text.Encoding.GetEncoding("GB2312"))) 
                    {
                        _WelcomeMessage = response.WelcomeMessage;
                        _ExitMessage = response.ExitMessage;
                        result = sr.ReadToEnd();
                        sr.Close();
                    }
                    try
                    {
                        //Declare Event
                        NewMessageEventArgs e = new NewMessageEventArgs("RESPONSE", response.StatusDescription, response.StatusCode.ToString());
                        //Raise Event
                        OnNewMessageReceived(this, e);
                    }
                    catch
                    {

                    }

                    datastream.Close();
                }
                response.Close();
            }
            return result;
        }

        /// <summary>
        /// Gets the size of an FTP request
        /// </summary>
        /// <param name="ftp"></param>
        /// <returns></returns>
        /// <remarks></remarks>
        private long GetSize(FtpWebRequest ftp)
        {
            long size;
            using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
            {
                size = response.ContentLength;
                response.Close();
            }

            return size;
        }
        #endregion

        #region "Properties"
        private string _hostname;
        /// <summary>
        /// Hostname
        /// </summary>
        /// <value></value>
        /// <remarks>Hostname can be in either the full URL format
        /// ftp://ftp.myhost.com or just ftp.myhost.com
        /// </remarks>
        public string Hostname
        {
            get
            {
                if (_hostname.StartsWith("ftp://"))
                {
                    return _hostname;
                }
                else
                {
                    return "ftp://" + _hostname;
                }
            }
            set
            {
                _hostname = value;
            }
        }
        private string _username;
        /// <summary>
        /// Username property
        /// </summary>
        /// <value></value>
        /// <remarks>Can be left blank, in which case 'anonymous' is returned</remarks>
        public string Username
        {
            get
            {
                return (_username == "" ? "anonymous" : _username);
            }
            set
            {
                _username = value;
            }
        }
        private string _password;
        public string Password
        {
            get
            {
                return _password;
            }
            set
            {
                _password = value;
            }
        }

        /// <summary>
        /// The CurrentDirectory value
        /// </summary>
        /// <remarks>Defaults to the root '/'</remarks>
        private string _currentDirectory = "/";
        public string CurrentDirectory
        {
            get
            {
                //return directory, ensure it ends with /
                return _currentDirectory + ((_currentDirectory.EndsWith("/")) ? "" : "/").ToString();
            }
            set
            {
                if (!value.StartsWith("/"))
                {
                    throw (new ApplicationException("Directory should start with /"));
                }
                _currentDirectory = value;
            }
        }


        #endregion

        #region Server Messages
        string _WelcomeMessage, _ExitMessage;
        public string WelcomeMessage
        {
            get
            {
                return _WelcomeMessage;
            }
            set
            {
                _WelcomeMessage = value;
            }
        }
        public string ExitMessage
        {
            get
            {
                return _ExitMessage;
            }
            set
            {
                _ExitMessage = value;
            }
        }
        #endregion

    }
    #endregion

    #region "FTP file info class"
    /// <summary>
    /// Represents a file or directory entry from an FTP listing
    /// </summary>
    /// <remarks>
    /// This class is used to parse the results from a detailed
    /// directory list from FTP. It supports most formats of
    /// </remarks>
    public class FTPfileInfo
    {

        //Stores extended info about FTP file

        #region "Properties"
        public string FullName
        {
            get
            {
                return Path + Filename;
            }
        }
        public string Filename
        {
            get
            {
                return _filename;
            }
        }
        public string Path
        {
            get
            {
                return _path;
            }
        }
        public DirectoryEntryTypes FileType
        {
            get
            {
                return _fileType;
            }
        }
        public long Size
        {
            get
            {
                return _size;
            }
        }
        public DateTime FileDateTime
        {
            get
            {
                return _fileDateTime;
            }
        }
        public string Permission
        {
            get
            {
                return _permission;
            }
        }
        public string Extension
        {
            get
            {
                int i = this.Filename.LastIndexOf(".");
                if (i >= 0 && i < (this.Filename.Length - 1))
                {
                    return this.Filename.Substring(i + 1);
                }
                else
                {
                    return "";
                }
            }
        }
        public string NameOnly
        {
            get
            {
                int i = this.Filename.LastIndexOf(".");
                if (i > 0)
                {
                    return this.Filename.Substring(0, i);
                }
                else
                {
                    return this.Filename;
                }
            }
        }
        private string _filename;
        private string _path;
        private DirectoryEntryTypes _fileType;
        private long _size;
        private DateTime _fileDateTime;
        private string _permission;

        #endregion

        /// <summary>
        /// Identifies entry as either File or Directory
        /// </summary>
        public enum DirectoryEntryTypes
        {
            File,
            Directory
        }

        /// <summary>
        /// Constructor taking a directory listing line and path
        /// </summary>
        /// <param name="line">The line returned from the detailed directory list</param>
        /// <param name="path">Path of the directory</param>
        /// <remarks></remarks>
        public FTPfileInfo(string line, string path)
        {
            //parse line
            Match m = GetMatchingRegex(line);
            if (m == null)
            {
                //failed
                throw (new ApplicationException("Unable to parse line: " + line));
            }
            else
            {
                _filename = m.Groups["name"].Value;
                _path = path;

                Int64.TryParse(m.Groups["size"].Value, out _size);
                //_size = System.Convert.ToInt32(m.Groups["size"].Value);

                _permission = m.Groups["permission"].Value;
                string _dir = m.Groups["dir"].Value;
                if (_dir != "" && _dir != "-")
                {
                    _fileType = DirectoryEntryTypes.Directory;
                }
                else
                {
                    _fileType = DirectoryEntryTypes.File;
                }

                try
                {
                    _fileDateTime = DateTime.Parse(m.Groups["timestamp"].Value);
                }
                catch (Exception)
                {
                    _fileDateTime = Convert.ToDateTime(null);
                }

            }
        }

        private Match GetMatchingRegex(string line)
        {
            Regex rx;
            Match m;
            for (int i = 0; i <= _ParseFormats.Length - 1; i++)
            {
                rx = new Regex(_ParseFormats[i]);
                m = rx.Match(line);
                if (m.Success)
                {
                    return m;
                }
            }
            return null;
        }

        #region "Regular expressions for parsing LIST results"
        /// <summary>
        /// List of REGEX formats for different FTP server listing formats
        /// </summary>
        /// <remarks>
        /// The first three are various UNIX/LINUX formats, fourth is for MS FTP
        /// in detailed mode and the last for MS FTP in 'DOS' mode.
        /// I wish VB.NET had support for Const arrays like C# but there you go
        /// </remarks>
        private static string[] _ParseFormats = new string[] { 
            "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\w+\\s+\\w+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{4})\\s+(?<name>.+)", 
            "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\d+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{4})\\s+(?<name>.+)", 
            "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\d+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{1,2}:\\d{2})\\s+(?<name>.+)", 
            "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})\\s+\\d+\\s+\\w+\\s+\\w+\\s+(?<size>\\d+)\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{1,2}:\\d{2})\\s+(?<name>.+)", 
            "(?<dir>[\\-d])(?<permission>([\\-r][\\-w][\\-xs]){3})(\\s+)(?<size>(\\d+))(\\s+)(?<ctbit>(\\w+\\s\\w+))(\\s+)(?<size2>(\\d+))\\s+(?<timestamp>\\w+\\s+\\d+\\s+\\d{2}:\\d{2})\\s+(?<name>.+)", 
            "(?<timestamp>\\d{2}\\-\\d{2}\\-\\d{2}\\s+\\d{2}:\\d{2}[Aa|Pp][mM])\\s+(?<dir>\\<\\w+\\>){0,1}(?<size>\\d+){0,1}\\s+(?<name>.+)" };
        #endregion
    }
    #endregion

    #region "FTP Directory class"
    /// <summary>
    /// Stores a list of files and directories from an FTP result
    /// </summary>
    /// <remarks></remarks>
    public class FTPdirectory : List<FTPfileInfo>
    {


        public FTPdirectory()
        {
            //creates a blank directory listing
        }

        /// <summary>
        /// Constructor: create list from a (detailed) directory string
        /// </summary>
        /// <param name="dir">directory listing string</param>
        /// <param name="path"></param>
        /// <remarks></remarks>
        public FTPdirectory(string dir, string path)
        {
            foreach (string line in dir.Replace("\n", "").Split(System.Convert.ToChar('\r')))
            {
                //parse
                if (line != "")
                {
                    this.Add(new FTPfileInfo(line, path));
                }
            }
        }

        /// <summary>
        /// Filter out only files from directory listing
        /// </summary>
        /// <param name="ext">optional file extension filter</param>
        /// <returns>FTPdirectory listing</returns>
        public FTPdirectory GetFiles(string ext)
        {
            return this.GetFileOrDir(FTPfileInfo.DirectoryEntryTypes.File, ext);
        }

        /// <summary>
        /// Returns a list of only subdirectories
        /// </summary>
        /// <returns>FTPDirectory list</returns>
        /// <remarks></remarks>
        public FTPdirectory GetDirectories()
        {
            return this.GetFileOrDir(FTPfileInfo.DirectoryEntryTypes.Directory, "");
        }

        //internal: share use function for GetDirectories/Files
        private FTPdirectory GetFileOrDir(FTPfileInfo.DirectoryEntryTypes type, string ext)
        {
            FTPdirectory result = new FTPdirectory();
            foreach (FTPfileInfo fi in this)
            {
                if (fi.FileType == type)
                {
                    if (ext == "")
                    {
                        result.Add(fi);
                    }
                    else if (ext == fi.Extension)
                    {
                        result.Add(fi);
                    }
                }
            }
            return result;

        }

        public bool FileExists(string filename)
        {
            foreach (FTPfileInfo ftpfile in this)
            {
                if (ftpfile.Filename == filename)
                {
                    return true;
                }
            }
            return false;
        }

        private const char slash = '/';

        public static string GetParentDirectory(string dir)
        {
            string tmp = dir.TrimEnd(slash);
            int i = tmp.LastIndexOf(slash);
            if (i > 0)
            {
                return tmp.Substring(0, i - 1);
            }
            else
            {
                throw (new ApplicationException("No parent for root"));
            }
        }
    }
    #endregion

    #region Events
    public class DownloadProgressChangedArgs : EventArgs
    {
        //Private Members
        private Int64 _BytesDownload;
        private Int64 _TotalBytes;

        //Constructor
        public DownloadProgressChangedArgs(Int64 BytesDownload, Int64 TotleBytes)
        {
            this._BytesDownload = BytesDownload;
            this._TotalBytes = TotleBytes;
        }

        //Public Members
        public Int64 BytesDownloaded { get { return _BytesDownload; } }
        public Int64 TotleBytes { get { return _TotalBytes; } }
    }

    public class DownloadCompletedArgs : EventArgs
    {
        //Private Members
        private bool _DownloadedCompleted;
        private string _DownloadStatus;

        //Constructor
        public DownloadCompletedArgs(string Status, bool Completed)
        {
            this._DownloadedCompleted = Completed;
            this._DownloadStatus = Status;
        }

        //Public Members
        public String DownloadStatus { get { return _DownloadStatus; } }
        public bool DownloadCompleted { get { return _DownloadedCompleted; } }
    }

    public class NewMessageEventArgs : EventArgs
    {
        //Private Members
        private string _Message;
        private string _StatusCode;
        private string _Type;

        //Constructor
        public NewMessageEventArgs(string Type, string Status, string Code)
        {
            this._Message = Status;
            this._StatusCode = Code;
            this._Type = Type;
        }

        //Public Members
        public string StatusMessage { get { return _Message; } }
        public string StatusCode { get { return _StatusCode; } }
        public string StatusType { get { return _Type; } }
    }

    public class UploadProgressChangedArgs : EventArgs
    {
        //Private Members
        private Int64 _BytesUpload;
        private Int64 _TotalBytes;

        //Constructor
        public UploadProgressChangedArgs(Int64 BytesUpload, Int64 TotleBytes)
        {
            this._BytesUpload = BytesUpload;
            this._TotalBytes = TotleBytes;
        }

        //Public Members
        public Int64 BytesUploaded { get { return _BytesUpload; } }
        public Int64 TotleBytes { get { return _TotalBytes; } }
    }

    public class UploadCompletedArgs : EventArgs
    {
        //Private Members
        private bool _UploadCompleted;
        private string _UploadStatus;

        //Constructor
        public UploadCompletedArgs(string Status, bool Completed)
        {
            this._UploadCompleted = Completed;
            this._UploadStatus = Status;
        }

        //Public Members
        public String UploadStatus { get { return _UploadStatus; } }
        public bool UploadCompleted { get { return _UploadCompleted; } }
    }
    #endregion
}

  测试代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using FTPLibrary;
using System.Diagnostics;
using System.Reflection;
using System.IO;


namespace ftpdemo
{

    /// <summary>
    /// 涂聚文 2017-08-01
    /// </summary>
    public partial class Form6 : Form
    {
        public FTPclient FtpClient;
        ListViewItem Message;
        string remotingFolder = System.Configuration.ConfigurationManager.AppSettings["remotingFolder"];  //远程ftp文件目录
        string localFolder = System.Configuration.ConfigurationManager.AppSettings["localFolder"];  //要下载到的本地目录
        string ftpServer = System.Configuration.ConfigurationManager.AppSettings["ftpServer"];  //ftp服务器
        string user = System.Configuration.ConfigurationManager.AppSettings["user"];  //用户名
        string pwd = System.Configuration.ConfigurationManager.AppSettings["pwd"];  //密码
        string port = System.Configuration.ConfigurationManager.AppSettings["port"];  //端口

        /// <summary>
        /// 
        /// </summary>
        /// <param name="byteCount"></param>
        /// <returns></returns>
        private string GetFileSize(double byteCount)
        {
            string size = "0 Bytes";
            if (byteCount >= 1073741824.0)
                size = String.Format("{0:##.##}", byteCount / 1073741824.0) + " GB";
            else if (byteCount >= 1048576.0)
                size = String.Format("{0:##.##}", byteCount / 1048576.0) + " MB";
            else if (byteCount >= 1024.0)
                size = String.Format("{0:##.##}", byteCount / 1024.0) + " KB";
            else if (byteCount > 0 && byteCount < 1024.0)
                size = byteCount.ToString() + " Bytes";

            return size;
        }
        /// <summary>
        /// Sets up the FTPClient for this Form.  Called from frmLogin.
        /// </summary>
        /// <param name="client">FTPclient on frmLogin is used to refrence the FtpClient here.</param>
        public void SetFtpClient(FTPclient client)
        {
            //Set FtpClient
            FtpClient = client;

            //Display the Welcome Message
            Message = new ListViewItem();
            Message.Text = DateTime.Now.ToLongTimeString() + " " + DateTime.Now.ToLongDateString();
            Message.SubItems.Add("Welcome Message");
            Message.SubItems.Add(FtpClient.WelcomeMessage);
            Message.SubItems.Add("No Code");
            Message.SubItems.Add("/");
            lstMessages.Items.Add(Message);

            //Setup OnMessageReceived Event
            FtpClient.OnNewMessageReceived += new FTPclient.NewMessageHandler(FtpClient_OnNewMessageReceived);

            //Open and Display Root Directory and Files/Folders in it
            foreach (FTPfileInfo folder in FtpClient.ListDirectoryDetail("/"))
            {
                ListViewItem item = new ListViewItem();
                item.Text = folder.Filename;
                if (folder.FileType == FTPfileInfo.DirectoryEntryTypes.Directory)
                    item.SubItems.Add("Folder");
                else
                    item.SubItems.Add("File");

                item.SubItems.Add(folder.FullName);
                item.SubItems.Add(folder.Permission);
                item.SubItems.Add(folder.FileDateTime.ToShortTimeString() + folder.FileDateTime.ToShortDateString());
                item.SubItems.Add(GetFileSize(folder.Size));
                lstRemoteSiteFiles.Items.Add(item);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="myObject"></param>
        /// <param name="e"></param>
        private void FtpClient_OnNewMessageReceived(object myObject, NewMessageEventArgs e)
        {
            //Display Meesage in lstMessages
            Message = new ListViewItem();
            Message.Text = DateTime.Now.ToLongTimeString() + " " + DateTime.Now.ToLongDateString();
            Message.SubItems.Add(e.StatusType);
            Message.SubItems.Add(e.StatusMessage);
            Message.SubItems.Add(e.StatusCode);
            Message.SubItems.Add(txtRemoteDirectory.Text);
            lstMessages.Items.Add(Message);

            this.lstMessages.EnsureVisible(this.lstMessages.Items.Count - 1);
        }
        /// <summary>
        /// Reload all Directories and Files in Current Directory
        /// </summary>
        private void RefreshDirectory()
        {
            //Clear all items
            lstRemoteSiteFiles.Items.Clear();

            //Open and Display Root Directory
            foreach (FTPfileInfo folder in FtpClient.ListDirectoryDetail(txtRemoteDirectory.Text))
            {
                ListViewItem item = new ListViewItem();
                item.Text = folder.Filename;
                if (folder.FileType == FTPfileInfo.DirectoryEntryTypes.Directory)
                    item.SubItems.Add("Folder");
                else
                    item.SubItems.Add("File");

                item.SubItems.Add(folder.FullName);
                item.SubItems.Add(folder.Permission);
                item.SubItems.Add(folder.FileDateTime.ToShortTimeString() + folder.FileDateTime.ToShortDateString());
                item.SubItems.Add(folder.Size.ToString());
                lstRemoteSiteFiles.Items.Add(item);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public Form6()
        {
            InitializeComponent();

            //Set Selected Directory
            txtRemoteDirectory.Text = "/";
            lstRemoteSiteFiles.FullRowSelect = true;  
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form6_Load(object sender, EventArgs e)
        {
            //Set FTP
            FTPclient objFtp = new FTPclient(ftpServer, user,pwd);
            objFtp.CurrentDirectory = "/";
            SetFtpClient(objFtp);

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lstRemoteSiteFiles_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lstRemoteSiteFiles_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (lstRemoteSiteFiles.Items.Count != 0)
            {
                try
                {
                    if (lstRemoteSiteFiles.SelectedItems[0].SubItems[1].Text == "File")
                    {
                        //Enable the buttons that are related to the FILE
                       // btnRename.Enabled = true;
                       // btnDownload.Enabled = true;
                        //Set Current Directory for Download
                        FtpClient.CurrentDirectory = txtRemoteDirectory.Text;
                        //Its a File, so Ask them if they want to Save it...
                        if (MessageBox.Show("Do you want to save this file: " + txtRemoteDirectory.Text + lstRemoteSiteFiles.SelectedItems[0].Text + "/" + "?", "Download File?", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button1) == DialogResult.Yes)
                        {
                            //Save the File to location
                            //downloadToolStripMenuItem_Click(this, e);
                        }
                    }
                    else if (lstRemoteSiteFiles.SelectedItems[0].SubItems[1].Text == "Folder") // Its a Directory
                    {
                        //Set Directory to txtRemoteDirectory.Text + selectedItem + "/"
                        //Result - /SelectedDirecotory/  -- good for navigation, keeping user informed and code :)
                        txtRemoteDirectory.Text += lstRemoteSiteFiles.SelectedItems[0].Text + "/";
                        lstRemoteSiteFiles.Items.Clear();

                        //Set Current Dir
                        FtpClient.CurrentDirectory = txtRemoteDirectory.Text;

                        //Get Files and Folders from Selected Direcotry
                        foreach (FTPfileInfo folder in FtpClient.ListDirectoryDetail(txtRemoteDirectory.Text))
                        {
                            ListViewItem item = new ListViewItem();
                            item.Text = folder.Filename;
                            if (folder.FileType == FTPfileInfo.DirectoryEntryTypes.Directory)
                                item.SubItems.Add("Folder");
                            else
                                item.SubItems.Add("File");

                            item.SubItems.Add(folder.FullName);
                            item.SubItems.Add(folder.Permission);
                            item.SubItems.Add(folder.FileDateTime.ToShortDateString()+" "+folder.FileDateTime.ToShortTimeString());
                            item.SubItems.Add(GetFileSize(folder.Size));
                            lstRemoteSiteFiles.Items.Add(item);
                        }
                    }
                }
                catch { }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lstRemoteSiteFiles_ItemDrag(object sender, ItemDragEventArgs e)
        {

        }
    }
}

  

posted @ 2017-08-02 16:36  ®Geovin Du Dream Park™  阅读(888)  评论(0编辑  收藏  举报