Ssh.Net Sftp Get a List of Directories and Files Recursively

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



Leave a reply



Submit