ASP.NET CORE: Using SFTP/FTP in ASP.NET CORE Projects





5.00/5 (3 votes)
Using SFTP/FTP 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
{
/*FluentFTP example : https://github.com/robinrodricks/FluentFTP
* Local Directory: @"C:\Files\Temp\file.csv"
* @"C:\Files\Temp"
* Ftp Directory(FluentFTP): @"/Files/Temp/file.csv"
* @"/Files/Temp/
*/
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();
}
}
/*actions*/
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
{
/* SshNet example : https://github.com/sshnet/SSH.NET
* Local Directory: @"C:\Files\Temp\file.csv"
* @"C:\Files\Temp"
* Ftp Directory(FluentFTP): @"/Files/Temp/file.csv"
* @"/Files/Temp/
*/
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();
}
}
/*actions*/
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:
/*Set your SFTP/FTP server details here*/
RemoteSystemSetting setting = new RemoteSystemSetting()
{
Host = "xx.xx.xx.xx", /*host ip*/
Port = 21, /*ftp:21, sftp:22*/
UserName = "xyz",
Password = "abc"
};
IRemoteFileSystemContext remote = new FtpRemoteFileSystem(setting);
/*to use SFTP remote = new SftpRemoteFileSystem(setting);*/
remote.Connect(); /*establish connection*/
remote.SetRootAsWorkingDirectory(); /*set root as work directory*/
remote.DownloadFile("C:\\1.txt", "/files/test/1.txt"); /*download file*/
remote.UploadFile("C:\\2.txt", "/files/test/2.txt"); /*upload upload file*/
/*others*/
bool isConnected = remote.IsConnected(); /*check connection done or not*/
remote.Disconnect(); /*stop connection*/
remote.Dispose(); /*dispose*/
remote.DirectoryExists("/files/test/"); /*check if directory exists or not*/
remote.CreateDirectoryIfNotExists("/files/test/"); /*create directory*/
remote.FileExists("/files/test/1.txt"); /*check if file exists or not*/
remote.DeleteFileIfExists("/files/test/1.txt"); /*delete file*/
remote.SetWorkingDirectory("/files/test"); /*set other directory as root*/
Future Works
Going to add them soon.
/*get all file names*/
/*get all directory names*/
/*download all files*/
/*upload all files*/
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
{
/// <summary>
/// string p1 = "c:\\temp\\";
/// string p2 = "\\subdir\\file\\";
/// to c:\temp\subdir\file
/// </summary>
public static string CombineDirectory
(string rootDirectoryPath, string childDirectoryPath)
{
rootDirectoryPath = rootDirectoryPath.TrimEnd('\\');
childDirectoryPath = childDirectoryPath.Trim('\\');
return Path.Combine(rootDirectoryPath, childDirectoryPath);
}
/// <summary>
/// string p1 = "c:\\temp\\";
/// string p2 = "\\file.text";
/// to c:\temp\file.text
/// </summary>
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);
}
/*file*/
public static void MoveFile(string fromFilePath, string toFilePath)
{
File.Move(fromFilePath, toFilePath);
}
public static void FileAppendAllText(string filePath, string contents)
{
/*create file if doesn't exist and add line*/
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
{
/// <summary>
/// string p1 = "/temp";
/// to /temp/
/// </summary>
public static string FtpDirectory(string rootDirectory)
{
rootDirectory = rootDirectory.Trim('/');
return string.Format(@"/{0}/", rootDirectory);
}
/// <summary>
/// string p1 = "/temp/";
/// string p2 = "/subdir/file/";
/// to /temp/subdir/file/
/// </summary>
public static string CombineDirectory(string rootDirectory, string childDirectory)
{
rootDirectory = rootDirectory.Trim('/');
childDirectory = childDirectory.Trim('/');
return string.Format(@"/{0}/{1}/", rootDirectory, childDirectory);
}
/// <summary>
/// string p1 = "/temp/";
/// string p2 = "file.text";
/// to /temp/file.text
/// </summary>
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
{
/*
https://stackoverflow.com/questions/1768198/how-do-i-get-the-computer-name-in-net
https://stackoverflow.com/questions/1233217/
difference-between-systeminformation-computername-environment-machinename-and
*/
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 appFileSystem.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