How to list directories using SSH.NET?
On Unix-like OSes, including Linux, directories are files - so your ListDirectory
result will return "files" (in the traditional sense) and directories combined. You can filter those out by checking IsDirectory
:
public List<String> GetFiles(string path)
{
using (SftpClient client = new SftpClient( _host, _port, _username, _password ) )
{
client.Connect();
return client
.ListDirectory( path )
.Where( f => !f.IsDirectory )
.Select( f => f.Name )
.ToList();
}
}
public List<String> GetDirectories(string path)
{
using (SftpClient client = new SftpClient( _host, _port, _username, _password ) )
{
client.Connect();
return client
.ListDirectory( path )
.Where( f => f.IsDirectory )
.Select( f => f.Name )
.ToList();
}
}
(I changed the return type to a concrete List<T>
because if ListDirectory
were to return a lazily-evaluated enumerable then the using()
block would invalidate the parent SftpClient
object before the operation had completed - the same reason you never return an IQueryable<T>
from within a using( DbContext )
)
Convert string or list to Renci.SshNet.Sftp
I fixed the issue just by concatenating(subdirectory to directory) and reassigning it to the directory.
using (var sftp = new SftpClient(host, 22, username, password))
{
sftp.Connect();
var directory = sftp.ListDirectory("/").ToList();
string FileName = "Dir1/abc.txt,file1.txt,file2.txt"
var fileNameList = FileName.Contains(',') ? FileName.Split(',') : String.IsNullOrEmpty(FileName) ? new string[0] : new string[1] { FileName };
HashSet<string> filenameHash = new HashSet<string>(fileNameList.Select(x => x));
directory.RemoveAll(x => !filenameHash.Contains(x.Name));
directory.RemoveAll(x => (!x.IsRegularFile));
string dir = "";
SftpFile fl = (SftpFile)Convert.ChangeType(dir, typeof(SftpFile));
foreach (var f in fileNameList)
{
if (f.Contains('/'))
{
string[] fl = f.Split("/");
var subdirectory = sftp.ListDirectory(f.Split('/')[0]).ToList();
directory = directory.Concat(subdirectory).ToList();
}
}
Counting SFTP files matching certain criteria using SSH.NET
The syntax you have in Where
method argument is not a valid lambda function, you miss the parameter list. It should be:
.Where(file => (file.Name != ".") && (file.Name != "..") && file.Name.EndsWith(fileextension))
Also, do not call ListDirectory
repeatedly, let alone in every iteration.
var files = client.ListDirectory(dirName);
files = files.Where(file => (file.Name != ".") && (file.Name != "..") && file.Name.EndsWith(fileextension));
int count = files.Count();
foreach (SftpFile file in files)
{
// ...
}
Downloading a directory using SSH.NET SFTP in C#
BeginDownloadFile
downloads a file. You cannot use it to download a folder. For that you need to download contained files one by one.
The following example uses synchronous download (DownloadFile
instead of BeginDownloadFile
) for simplicity. After all, you are synchronously waiting for asynchronous download to complete anyway. To implement a progress bar with synchronous download, see Displaying progress of file download in a ProgressBar with SSH.NET.
public static void DownloadDirectory(
SftpClient sftpClient, string sourceRemotePath, string destLocalPath)
{
Directory.CreateDirectory(destLocalPath);
IEnumerable<SftpFile> files = sftpClient.ListDirectory(sourceRemotePath);
foreach (SftpFile file in files)
{
if ((file.Name != ".") && (file.Name != ".."))
{
string sourceFilePath = sourceRemotePath + "/" + file.Name;
string destFilePath = Path.Combine(destLocalPath, file.Name);
if (file.IsDirectory)
{
DownloadDirectory(sftpClient, sourceFilePath, destFilePath);
}
else
{
using (Stream fileStream = File.Create(destFilePath))
{
sftpClient.DownloadFile(sourceFilePath, fileStream);
}
}
}
}
}
Copying files to remote and creating directories and recursive copying
First, if you have SCP, you most likely have SFTP too. SFTP and SCP both typically come with an SSH server. So if you have SFTP too, use that, as SFTP is a complete protocol that supports not only file transfers (unlike SCP) but also all other file operations, like directory creation.
Alternatively, as you have found out, if you have a shell access (what is also rather common, if you have SCP access), use shell commands for creating the directly (that is mkdir
on Linux shells).
Renci SSH.NET: Is it possible to create a folder containing a subfolder that does not exist
There's no other way.
Just iterate directory levels, testing each level using SftpClient.GetAttributes
and create the levels that do not exist.
static public void CreateDirectoryRecursively(this SftpClient client, string path)
{
string current = "";
if (path[0] == '/')
{
path = path.Substring(1);
}
while (!string.IsNullOrEmpty(path))
{
int p = path.IndexOf('/');
current += '/';
if (p >= 0)
{
current += path.Substring(0, p);
path = path.Substring(p + 1);
}
else
{
current += path;
path = "";
}
try
{
SftpFileAttributes attrs = client.GetAttributes(current);
if (!attrs.IsDirectory)
{
throw new Exception("not directory");
}
}
catch (SftpPathNotFoundException)
{
client.CreateDirectory(current);
}
}
}
Download SFTP files that starts with prefix name using SSH.NET in C#
Start with the code from the following question and add the additional constraint on the file name prefix.
Downloading a directory using SSH.NET SFTP in C#
const string prefix = "POS_ETH_SE7";
IEnumerable<SftpFile> files = client.ListDirectory(remotePath);
files = files.Where(file => file.Name.StartsWith(prefix));
foreach (SftpFile file in files)
{
string pathLocalFile = Path.Combine(localPath, file.Name);
using (var stream = File.Create(pathLocalFile))
{
client.DownloadFile(file.FullName, stream);
}
// If you want to archive the downloaded files:
string archivePath = remoteMoveFileToPath + "/" + file.Name;
client.RenameFile(file.FullName, archivePath);
}
Or use a more powerful SFTP library. For example with my WinSCP .NET assembly, you can do the same with a single call to Session.GetFilesToDirectory
:
session.GetFilesToDirectory(remotePath, localPath, prefix + "*").Check();
Related Topics
Replace Single Backslash With Double Backslash
Removing Text Between 2 Strings
Get Value from Jtoken That May Not Exist (Best Practices)
Redirect from Controller to Another View of Another Controller
Convert a List of Objects from One Type to Another Using Lambda Expression
Localhost Port Is Changing in Visual Studio 2017
How to Test If a Instance of a Class Is a Specific Generic Type
Getting the Latest File Modified from Azure Blob
How to Perform Join Between Multiple Tables in Linq Lambda
How to Correctly Write Parallel.For With Async Methods
How to Remove Windows Credentials
Passing Datetimeoffset as Webapi Query String
Reference External Dll in .Net Core Project
Asp.Net Core, Change Default Redirect for Unauthorized
Kill Child Process When Parent Process Is Killed
Setting Connection String With Username and Password in Asp.Core MVC
Automatically Update Values in Database from Datagridview
How to Create ASP.NET Identity Tables in an Already Created Database Using Code First