Search code examples
asp.netftpsftpftps

Upload file to Secured FTP in ASP.NET


I want to upload files to FTPS and SFTP. My code is currently using FtpWebRequest object to upload to FTP. What changes or class should I use to upload to FTP, FTPS and SFTP servers?


Solution

  • SFTP is not a built-in protocol for .NET, you'll have to use a third-party library, like SharpSSH; however, FTP and FTPS are. There are a number of third-party libraries both commercial and OpenSource (SSH Factory for .NET , Rebex SFTP for .NET/.NET CF, SharpSSH - A Secure Shell (SSH) library for .NET, Compare SFTP (SSH File Transfer Protocol) components for .NET (C#, VB.NET) - SecureBlackbox®) and you'll need to do some research to determine which one will best suit your needs.

    Here's a sample console app I wrote that does FTP and FTPS using the .NET Framework's FtpWebRequest:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    
    namespace FtpSslTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                string server = null;
    
                do
                {
                    Console.Write("Enter the server to connect to: ");
                    server = Console.ReadLine();
                } while (IsServerValid(server) == false);
    
                UriBuilder ftpUrl = new UriBuilder("ftp", server);
    
                bool useSsl = GetYesNo("Use SSL?");
                bool allowInvalidCertificate = false;
                if (useSsl)
                {
                    allowInvalidCertificate = GetYesNo("Allow invalid SSL certificate?");
                }
    
                bool useActiveFtp = GetYesNo("Use Active FTP?");
    
                string path = null;
    
                do
                {
                    Console.Write("Enter the path: ");
                    path = Console.ReadLine();
                } while (IsValidPath(path) == false);
    
                ftpUrl.Path = path;
    
                Console.Write("Enter the user name: ");
                string userName = Console.ReadLine();
    
                string password = GetPasswordFromUser();
    
                Console.WriteLine();
                Console.WriteLine();
    
                List<string> directoryContents = null;
                try
                {
                    directoryContents = DisplayDirectoryContents(ftpUrl.ToString(), userName, password, useSsl, allowInvalidCertificate, useActiveFtp, false);
                }
                catch (WebException ex)
                {
                    Console.WriteLine("The request failed with status {0}. {1}", ex.Status, ex.Message);
                }
                catch (Exception ex)
                {
                    Console.Error.WriteLine(ex.ToString());
                }
    
                if (directoryContents != null && directoryContents.Count == 1)
                {
                    bool saveFile = GetYesNo(string.Format("Download the file {0} from {1}? ", directoryContents[0], server));
    
                    if (saveFile)
                    {
                        string savePath = null;
    
                        do
                        {
                            Console.Write("Enter a local path to save the file: ");
                            savePath = Console.ReadLine();
                        } while (!IsValidPath(savePath));
    
                        try
                        {
                            DownloadFileFromServer(ftpUrl.ToString(), userName, password, useSsl, allowInvalidCertificate, useActiveFtp, savePath);
                        }
                        catch (WebException ex)
                        {
                            Console.WriteLine("The request failed with status {0}. {1}", ex.Status, ex.Message);
                        }
                        catch (Exception ex)
                        {
                            Console.Error.WriteLine(ex.ToString());
                        }
                    }
                }
            }
    
            private static bool GetYesNo(string message)
            {
                Console.Write("{0} (Y/N) ", message);
    
                string input = null;
    
                do
                {
                    input = new string(Console.ReadKey(true).KeyChar, 1);
                } while (!input.Equals("Y", StringComparison.CurrentCultureIgnoreCase) && !input.Equals("N", StringComparison.CurrentCultureIgnoreCase));
    
                Console.WriteLine(input);
    
                return input.Equals("Y", StringComparison.CurrentCultureIgnoreCase);
            }
    
            private static bool IsValidPath(string path)
            {
                bool validPath = false;
    
                validPath = path != null && path.IndexOfAny(Path.GetInvalidPathChars()) < 0;
    
                if (validPath == false)
                {
                    Console.WriteLine("You must enter a valid path.");
                }
    
                return validPath;
            }
    
            private static bool IsServerValid(string server)
            {
                bool serverValid = false;
    
                if (!string.IsNullOrEmpty(server))
                {
                    try
                    {
                        IPAddress[] addresses = Dns.GetHostAddresses(server);
                        serverValid = (addresses != null && addresses.Length > 0);
                    }
                    catch (SocketException ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
                else
                {
                    Console.WriteLine("You must provide a valid host name or IP address.");
                }
    
                return serverValid;
            }
    
            private static string GetPasswordFromUser()
            {
                Console.Write("Enter the password: ");
                StringBuilder password = new StringBuilder();
    
                char readChar = '\x00';
                while (readChar != '\r')
                {
                    readChar = Console.ReadKey(true).KeyChar;
    
                    if (readChar == '\b')
                    {
                        if (password.Length > 0)
                        {
                            password.Length--;
                            Console.Write("\b \b");
                        }
                    }
                    else if (readChar != '\r')
                    {
                        Console.Write('*');
                        password.Append(readChar);
                    }
                }
    
                return password.ToString();
            }
    
            public static bool ServicePointManager_ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
            {
                bool allowCertificate = true;
    
                if (sslPolicyErrors != SslPolicyErrors.None)
                {
                    Console.WriteLine("Accepting the certificate with errors:");
                    if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch)
                    {
                        Console.WriteLine("\tThe certificate subject {0} does not match.", certificate.Subject);
                    }
    
                    if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors)
                    {
                        Console.WriteLine("\tThe certificate chain has the following errors:");
                        foreach (X509ChainStatus chainStatus in chain.ChainStatus)
                        {
                            Console.WriteLine("\t\t{0}", chainStatus.StatusInformation);
    
                            if (chainStatus.Status == X509ChainStatusFlags.Revoked)
                            {
                                allowCertificate = false;
                            }
                        }
                    }
    
                    if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) == SslPolicyErrors.RemoteCertificateNotAvailable)
                    {
                        Console.WriteLine("No certificate available.");
                        allowCertificate = false;
                    }
    
                    Console.WriteLine();
                }
    
                return allowCertificate;
            }
    
            private static FtpWebRequest CreateFtpWebRequest(string ftpUrl, string userName, string password, bool useSsl, bool allowInvalidCertificate, bool useActiveFtp)
            {
                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUrl);
                request.Credentials = new NetworkCredential(userName, password);
    
                if (useSsl)
                {
                    request.EnableSsl = true;
    
                    if (allowInvalidCertificate)
                    {
                        ServicePointManager.ServerCertificateValidationCallback = ServicePointManager_ServerCertificateValidationCallback;
                    }
                    else
                    {
                        ServicePointManager.ServerCertificateValidationCallback = null;
                    }
                }
    
                request.UsePassive = !useActiveFtp;
    
                return request;
            }
    
            private static List<string> DisplayDirectoryContents(string ftpUrl, string userName, string password, bool useSsl, bool allowInvalidCertificate, bool useActiveFtp, bool detailed)
            {
                List<string> directoryContents = new List<string>();
    
                FtpWebRequest request = CreateFtpWebRequest(ftpUrl, userName, password, useSsl, allowInvalidCertificate, useActiveFtp);
    
                if (detailed)
                {
                    request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                }
                else
                {
                    request.Method = WebRequestMethods.Ftp.ListDirectory;
                }
    
                Stopwatch stopwatch = new Stopwatch();
                long bytesReceived = 0;
    
                stopwatch.Start();
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    Console.WriteLine(response.BannerMessage);
                    Console.WriteLine(response.WelcomeMessage);
                    Console.WriteLine(response.StatusDescription);
    
                    using (Stream responseStream = response.GetResponseStream())
                    using (StreamReader responseStreamReader = new StreamReader(responseStream))
                    {
                        while (!responseStreamReader.EndOfStream)
                        {
                            string directoryEntry = responseStreamReader.ReadLine();
                            Console.WriteLine(directoryEntry);
                            directoryContents.Add(directoryEntry);
                        }
                    }
    
                    Console.WriteLine(response.ExitMessage);
                }
                stopwatch.Stop();
    
                Console.WriteLine();
                Console.WriteLine("{0} bytes received in {1} seconds.", bytesReceived, stopwatch.ElapsedMilliseconds / 1000.0);
    
                return directoryContents;
            }
    
            private static List<string> ListDirectoryContents(string ftpUrl, string userName, string password, bool useSsl, bool allowInvalidCertificate, bool useActiveFtp, bool detailed)
            {
                List<string> directoryContents = new List<string>();
    
                FtpWebRequest request = CreateFtpWebRequest(ftpUrl, userName, password, useSsl, allowInvalidCertificate, useActiveFtp);
    
                if (detailed)
                {
                    request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
                }
                else
                {
                    request.Method = WebRequestMethods.Ftp.ListDirectory;
                }
    
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    using (Stream responseStream = response.GetResponseStream())
                    using (StreamReader responseStreamReader = new StreamReader(responseStream))
                    {
                        while (!responseStreamReader.EndOfStream)
                        {
                            string directoryEntry = responseStreamReader.ReadLine();
                            directoryContents.Add(directoryEntry);
                        }
                    }
    
                }
    
                return directoryContents;
            }
    
            private static void DownloadFileFromServer(string ftpUrl, string userName, string password, bool useSsl, bool allowInvalidCertificate, bool useActiveFtp, string savePath)
            {
                FtpWebRequest request = CreateFtpWebRequest(ftpUrl, userName, password, useSsl, allowInvalidCertificate, useActiveFtp);
    
                request.Method = WebRequestMethods.Ftp.DownloadFile;
    
                Stopwatch stopwatch = new Stopwatch();
                long bytesReceived = 0;
    
                stopwatch.Start();
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    Console.WriteLine(response.BannerMessage);
                    Console.WriteLine(response.WelcomeMessage);
                    Console.WriteLine(response.StatusDescription);
    
                    using (Stream responseStream = response.GetResponseStream())
                    using (FileStream saveFileStream = File.OpenWrite(savePath))
                    {
                        // Note that this method call requires .NET 4.0 or higher. If using an earlier version it will need to be replaced.
                        responseStream.CopyTo(saveFileStream);
                    }
    
                    bytesReceived = response.ContentLength;
                    Console.WriteLine(response.ExitMessage);
                }
                stopwatch.Stop();
    
                Console.WriteLine();
                Console.WriteLine("{0} bytes received in {1} seconds.", bytesReceived, stopwatch.ElapsedMilliseconds / 1000.0);
            }
    
            private static void UploadFileToServer(string ftpUrl, string userName, string password, bool useSsl, bool allowInvalidCertificate, bool useActiveFtp, string filePath)
            {
                FtpWebRequest request = CreateFtpWebRequest(ftpUrl, userName, password, useSsl, allowInvalidCertificate, useActiveFtp);
    
                request.Method = WebRequestMethods.Ftp.UploadFile;
    
                Stopwatch stopwatch = new Stopwatch();
                long bytesReceived = 0;
    
                stopwatch.Start();
                long bytesSent = 0;
                using (Stream requestStream = request.GetRequestStream())
                using (FileStream uploadFileStream = File.OpenRead(filePath))
                {
                    // Note that this method call requires .NET 4.0 or higher. If using an earlier version it will need to be replaced.
                    uploadFileStream.CopyTo(requestStream);
                    bytesSent = uploadFileStream.Position;
                }
    
                using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
                {
                    Console.WriteLine(response.BannerMessage);
                    Console.WriteLine(response.WelcomeMessage);
                    Console.WriteLine(response.StatusDescription);
    
                    bytesReceived = response.ContentLength;
                    Console.WriteLine(response.ExitMessage);
                }
                stopwatch.Stop();
    
                Console.WriteLine();
                Console.WriteLine("{0} bytes sent in {1} seconds.", bytesSent, stopwatch.ElapsedMilliseconds / 1000.0);
            }
        }
    }
    

    You can also get detailed tracing for debugging purposes by using the following config file with the sample application:

    <?xml version="1.0"?>
    <configuration>
      <system.diagnostics>
        <sources>
          <source name="System.Net">
            <listeners>
              <add name="TraceFile"/>
            </listeners>
          </source>
          <source name="System.Net.Sockets" maxdatasize="1024">
            <listeners>
              <add name="TraceFile"/>
            </listeners>
          </source>
        </sources>
        <sharedListeners>
          <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener" initializeData="System.Net.trace.log" traceOutputOptions="DateTime"/>
        </sharedListeners>
        <switches>
          <add name="System.Net" value="Verbose"/>
          <!--<add name="System.Net.Sockets" value="Verbose"/>-->
        </switches>
        <trace autoflush="true" />
      </system.diagnostics>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
      </startup>
    </configuration>