Saturday, December 1, 2012

Simple Ftp Client

I had to put together an ftp client to test some application today - nothing much just needed the listing and upload features.

Below is what i came up with:

public class FtpClient
    {
        private const string FtpProtocolHeader = "ftp://";
        private const string EscapedForwardSlash = "%2f";

        private string requestUriString;
        public string RequestUriString
        {
            get { return requestUriString; }
        }

        private string UserName { get; set; }

        private SecureString Password { get; set; }

        private FtpClient() { }

        public FtpClient(string uri, string userId, string password)
        {
            requestUriString = BuildUri(uri);
            UserName = userId;
            char[] passwordChars = password.ToCharArray();
            Password = new SecureString();
            foreach (char c in passwordChars)
                Password.AppendChar(c);
        }

        private static string BuildUri(string uri)
        {
            if (!System.Uri.IsWellFormedUriString(uri, UriKind.Absolute))
                throw new ArgumentException("Parameter is not well formed", "uri");

            if (!uri.StartsWith(FtpProtocolHeader, StringComparison.OrdinalIgnoreCase))
                uri = FtpProtocolHeader + uri;

            if (!uri.EndsWith("/", StringComparison.OrdinalIgnoreCase))
                uri += "/";
            return uri;
        }

        public FtpClient(string uri, string userId, SecureString password)
        {
            requestUriString = BuildUri(uri);
            UserName = userId;
            Password = password;
        }

        public IEnumerable DirectoryListing()
        {
            return DirectoryListing(null);
        }

        
        public IEnumerable DirectoryListing(string targetDir)
        {
            if (!string.IsNullOrWhiteSpace(targetDir))
            {
                BuildRequestUri(targetDir);
            }
            try
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(RequestUriString);
                request.Proxy = null;

                request.Method = WebRequestMethods.Ftp.ListDirectory;
                request.Credentials = new NetworkCredential(UserName, Password);

                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    {
                        if (responseStream == null)
                            return null;
                        List result;
                        using (var reader = new StreamReader(responseStream))
                        {
                            result = new List();

                            while (!reader.EndOfStream)
                            {
                                result.Add(reader.ReadLine());
                            }
                        }
                        return result;
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

        private void BuildRequestUri(string targetDir)
        {
            Debug.Assert(requestUriString != null, "requestUriString != null");
            if (!String.IsNullOrWhiteSpace(targetDir) && targetDir.StartsWith("/"))
            {
                targetDir = targetDir.Substring(1);
                requestUriString += string.Format("{0}{1}", EscapedForwardSlash, targetDir);
            }

        }

        
        public bool UploadFile(string fileSourcePath, string targetDir)
        {
            if (!File.Exists(fileSourcePath))
            {
                throw new ArgumentException("File path is invalid", "fileSourcePath");
            }
            string fileName = Path.GetFileName(fileSourcePath);
            BuildRequestUri(targetDir);
            try
            {
                // Get the object used to communicate with the server.
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(RequestUriString + fileName);

                request.UseBinary = true;
                request.Proxy = null;
                request.Method = WebRequestMethods.Ftp.UploadFile;

                // use the credentials
                request.Credentials = new NetworkCredential(UserName, Password);
                byte[] fileContent = File.ReadAllBytes(fileSourcePath);
                request.ContentLength = fileContent.Length;

                using (Stream requestStream = request.GetRequestStream())
                {
                    requestStream.Write(fileContent, 0, fileContent.Length);
                }

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

                return (response.StatusCode == FtpStatusCode.ClosingData);
            }
            catch
            {
                throw;
            }
        }
    }
Ran into an issue setting the root directory and it took me a while to understand it. From MSDN:

The URI may be relative or absolute. If the URI is of the form "ftp://contoso.com/%2fpath" (%2f is an escaped '/'), then the URI is absolute, and the current directory is /path. If, however, the URI is of the form "ftp://contoso.com/path", first the .NET Framework logs into the FTP server (using the user name and password set by the Credentials property), then the current directory is set to /path.


Of course, it doesn't do much at the moment, I intend to add some more commands to it soon. I also want to try out an async upload option.

No comments:

Post a Comment