This post is going to demonstrate how to do SFTP/FTP interactions and operations in ASP.NET CORE projects.
Background
In this article, we will check FTP/SFTP usages in ASP.NET CORE projects. We are going to create some FTP/SFTP client class to connect and do operations on FTP/SFTP systems.
The operation includes:
- Connecting/disconnecting with the server
- Uploading/downloading files
- Checking files/directories
- Creating/deleting files/directories
- And others
Common
Here are some common Interfaces to create some adapter classes for SFTP and FTP clients.
using System;
namespace FileSystem.Core.Remote
{
public interface IFileSystem
{
bool FileExists(string filePath);
bool DirectoryExists(string directoryPath);
void CreateDirectoryIfNotExists(string directoryPath);
void DeleteFileIfExists(string filePath);
}
public interface IRemoteFileSystemContext : IFileSystem, IDisposable
{
bool IsConnected();
void Connect();
void Disconnect();
void SetWorkingDirectory(string path);
void SetRootAsWorkingDirectory();
void UploadFile(string localFilePath, string remoteFilePath);
void DownloadFile(string localFilePath, string remoteFilePath);
string ServerDetails();
}
}
FTP
For FTP works, we are going to use FluentFTP. We can add the below code to our .csproj file or install it using NuGet.
<ItemGroup>
<PackageReference Include="FluentFTP" Version="19.2.2" />
</ItemGroup>
Let's create the adapter
class by implementing the interface.
using FluentFTP;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
namespace FileSystem.Core.Remote
{
public abstract class FtpContext : IRemoteFileSystemContext
{
protected IFtpClient FtpClient { get; set; }
public void Connect()
{
FtpClient.Connect();
}
public void Disconnect()
{
FtpClient.Disconnect();
}
public void Dispose()
{
if (FtpClient != null && !FtpClient.IsDisposed)
{
FtpClient.Dispose();
}
}
public bool FileExists(string filePath)
{
return FtpClient.FileExists(filePath);
}
public void DeleteFileIfExists(string filePath)
{
if (!FileExists(filePath))
{
FtpClient.DeleteFile(filePath);
}
}
public void UploadFile(string localFilePath, string remoteFilePath)
{
FtpClient.UploadFile(localFilePath, remoteFilePath);
}
public bool DirectoryExists(string directoryPath)
{
return FtpClient.DirectoryExists(directoryPath);
}
public void CreateDirectoryIfNotExists(string directoryPath)
{
if (!DirectoryExists(directoryPath))
{
FtpClient.CreateDirectory(directoryPath);
}
}
public void DownloadFile(string localFilePath, string remoteFilePath)
{
FtpClient.DownloadFile(localFilePath, remoteFilePath);
}
public bool IsConnected()
{
return FtpClient.IsConnected;
}
public void SetWorkingDirectory(string directoryPath)
{
FtpClient.SetWorkingDirectory(directoryPath);
}
public void SetRootAsWorkingDirectory()
{
SetWorkingDirectory("");
}
public abstract string ServerDetails();
}
}
Inheriting the adapter
class to set connection details from setting:RemoteSystemSetting
object.
using FileSystem.Core.Remote;
using FluentFTP;
namespace ConsoleApp.Test
{
class FtpRemoteFileSystem : FtpContext
{
private string _serverDetails;
public FtpRemoteFileSystem(RemoteSystemSetting setting)
{
_serverDetails = FtpHelper.ServerDetails
(setting.Host, setting.Port.ToString(), setting.UserName, setting.Type);
FtpClient = new FtpClient(setting.Host);
FtpClient.Credentials = new System.Net.NetworkCredential
(setting.UserName, setting.Password);
FtpClient.Port = setting.Port;
}
public override string ServerDetails()
{
return _serverDetails;
}
}
}
SFTP
For FTP works, we are going to use SSH.NET. We can add below code to our .csproj file or install it using NuGet.
<ItemGroup>
<PackageReference Include="SSH.NET" Version="2016.1.0" />
</ItemGroup>
Let's create the adapter
class by implementing the interface.
using FluentFTP;
using Renci.SshNet;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
namespace FileSystem.Core.Remote
{
public abstract class SftpContext : IRemoteFileSystemContext
{
protected SftpClient SftpClient { get; set; }
public void Connect()
{
SftpClient.Connect();
}
public void Disconnect()
{
SftpClient.Disconnect();
}
public void Dispose()
{
if (SftpClient != null)
{
SftpClient.Dispose();
}
}
public bool FileExists(string filePath)
{
return SftpClient.Exists(filePath);
}
public void DeleteFileIfExists(string filePath)
{
if (!FileExists(filePath))
{
SftpClient.DeleteFile(filePath);
}
}
public void UploadFile(string localFilePath, string remoteFilePath)
{
var fileStream = new FileStream(localFilePath, FileMode.Open);
SftpClient.UploadFile(fileStream, remoteFilePath);
}
public bool DirectoryExists(string directoryPath)
{
return SftpClient.Exists(directoryPath);
}
public void CreateDirectoryIfNotExists(string directoryPath)
{
if (!DirectoryExists(directoryPath))
{
SftpClient.CreateDirectory(directoryPath);
}
}
public void DownloadFile(string localFilePath, string remoteFilePath)
{
using (Stream fileStream = File.Create(localFilePath))
{
SftpClient.DownloadFile(remoteFilePath, fileStream);
}
}
public bool IsConnected()
{
return SftpClient.IsConnected;
}
public void SetWorkingDirectory(string directoryPath)
{
SftpClient.ChangeDirectory(directoryPath);
}
public void SetRootAsWorkingDirectory()
{
SetWorkingDirectory("");
}
public abstract string ServerDetails();
}
}
Inheriting the adapter
class to set connection details from setting:RemoteSystemSetting
object.
using FileSystem.Core.Remote;
using Renci.SshNet;
namespace ConsoleApp.Test
{
public class SftpRemoteFileSystem : SftpContext
{
private string _serverDetails;
public SftpRemoteFileSystem(RemoteSystemSetting setting)
{
_serverDetails = FtpHelper.ServerDetails
(setting.Host, setting.Port.ToString(), setting.UserName, setting.Type);
var connectionInfo = new ConnectionInfo
(setting.Host, setting.Port, setting.UserName,
new PasswordAuthenticationMethod(setting.UserName, setting.Password));
SftpClient = new SftpClient(connectionInfo);
}
public override string ServerDetails()
{
return _serverDetails;
}
}
}
Using the Code
Here are the uses of the SFTP/FTP adapter
classes:
RemoteSystemSetting setting = new RemoteSystemSetting()
{
Host = "xx.xx.xx.xx",
Port = 21,
UserName = "xyz",
Password = "abc"
};
IRemoteFileSystemContext remote = new FtpRemoteFileSystem(setting);
remote.Connect();
remote.SetRootAsWorkingDirectory();
remote.DownloadFile("C:\\1.txt", "/files/test/1.txt");
remote.UploadFile("C:\\2.txt", "/files/test/2.txt");
bool isConnected = remote.IsConnected();
remote.Disconnect();
remote.Dispose();
remote.DirectoryExists("/files/test/");
remote.CreateDirectoryIfNotExists("/files/test/");
remote.FileExists("/files/test/1.txt");
remote.DeleteFileIfExists("/files/test/1.txt");
remote.SetWorkingDirectory("/files/test");
Future Works
Going to add them soon.
Other Helper Class
Local File System Helper
This helper class can be used to manage the local path, directory, and file.
using System.IO;
namespace ConsoleApp.Test
{
public class FileSystemHelper
{
public static string CombineDirectory
(string rootDirectoryPath, string childDirectoryPath)
{
rootDirectoryPath = rootDirectoryPath.TrimEnd('\\');
childDirectoryPath = childDirectoryPath.Trim('\\');
return Path.Combine(rootDirectoryPath, childDirectoryPath);
}
public static string CombineFile(string rootDirectoryPath, string filePathOrName)
{
rootDirectoryPath = rootDirectoryPath.TrimEnd('\\');
filePathOrName = filePathOrName.Trim('\\');
return Path.Combine(rootDirectoryPath, filePathOrName);
}
public static void CreateDirectoryIfNotExists(string directoryPath)
{
if (!DirectoryExists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
}
public static void DeleteFileIfExists(string filePath)
{
if (FileExists(filePath))
{
File.Delete(filePath);
}
}
public static bool DirectoryExists(string directoryPath)
{
return Directory.Exists(directoryPath);
}
public static bool FileExists(string filePath)
{
return File.Exists(filePath);
}
public static void MoveFile(string fromFilePath, string toFilePath)
{
File.Move(fromFilePath, toFilePath);
}
public static void FileAppendAllText(string filePath, string contents)
{
File.AppendAllText(filePath, contents);
}
}
}
Remote FTP/SFTP File System Helper
This helper class can be used to manage the FTP/SFTP server path, directory, and file.
using System;
namespace ConsoleApp.Test
{
public class FtpHelper
{
public static string FtpDirectory(string rootDirectory)
{
rootDirectory = rootDirectory.Trim('/');
return string.Format(@"/{0}/", rootDirectory);
}
public static string CombineDirectory(string rootDirectory, string childDirectory)
{
rootDirectory = rootDirectory.Trim('/');
childDirectory = childDirectory.Trim('/');
return string.Format(@"/{0}/{1}/", rootDirectory, childDirectory);
}
public static string CombineFile(string rootDirectory, string filePathOrName)
{
rootDirectory = rootDirectory.Trim('/'); ;
filePathOrName = filePathOrName.Trim('/'); ;
return string.Format(@"/{0}/{1}", rootDirectory, filePathOrName);
}
public static string ServerDetails
(string host, string port, string userName, string type = "FTP")
{
return String.Format("Type: '{3}' Host:'{0}' Port:'{1}'
User:'{2}'", host, port, userName, type);
}
}
}
Local Machine Details Helper
This helper class can be used to get current machine details.
using System;
namespace ConsoleApp.Test
{
public class LocalMachineHelper
{
public static string ServerDetails()
{
string machineName = String.Empty;
string hostName = String.Empty;
string computerName = String.Empty;
try
{
machineName = Environment.MachineName;
hostName = System.Net.Dns.GetHostName();
computerName = Environment.GetEnvironmentVariable("COMPUTERNAME");
}
catch (Exception ex)
{
}
string details = String.Format("MachineName:'{0}' HostName:'{1}'
ComputerName:'{2}'", machineName, hostName, computerName);
return details;
}
}
}
About the Source Code
It a Visual Studio 2017 solution and ASP.NET Core 2.2 projects.
ConsoleApp.Test
: console app FileSystem.Core
: SFTP/FTP codes here
The code may throw unexpected errors for untested inputs. If any, just let me know.
How to do the Same for ASP.NET?
History
- 14th July, 2020: Initial version