我正在使用Renci.SshNet库通过SFTP递归获取文件和目录列表。我能够连接到SFTP站点,但我不知道如何在C#中递归获取文件和目录的列表。我没有找到任何有用的示例。
是否有人尝试过这个?如果有,能否发布一些样例代码关于如何递归获取这些文件和文件夹。
谢谢,
Prav
由于 ChangeDirectory
和 ListDirectory
之间的交互不符合预期,因此该库存在一些怪异之处,使得这个递归列表变得棘手。
下面的代码并不会列出 /home 目录中的文件,而是列出了根目录 / 的文件:
sftp.ChangeDirectory("home");
sftp.ListDirectory("").Select (s => s.FullName);
以下代码无法工作,并返回SftpPathNotFoundException异常:
sftp.ChangeDirectory("home");
sftp.ListDirectory("home").Select (s => s.FullName);
以下是列出 /home 目录中文件的正确方法
sftp.ChangeDirectory("/");
sftp.ListDirectory("home").Select (s => s.FullName);
如果你问我,这相当疯狂。使用ChangeDirectory
方法设置默认目录对ListDirectory
方法没有影响,除非在该方法的参数中指定一个文件夹。似乎应该为此编写一个错误报告。
因此,当您编写递归函数时,您需要先设置默认目录,然后在遍历文件夹时在ListDirectory
调用中更改目录。列表返回一个SftpFiles的可枚举项。这些可单独检查是否IsDirectory == true
。请注意,列表也会返回.
和..
条目(它们是目录)。如果要避免无限循环,您将要跳过这些条目。 :-)
编辑2/23/2018
我正在回顾我的一些旧答案,并为上面的答案道歉并提供以下可行代码。请注意,此示例不需要ChangeDirectory
,因为它使用Fullname
作为ListDirectory
的参数:
void Main()
{
using (var client = new Renci.SshNet.SftpClient("sftp.host.com", "user", "password"))
{
var files = new List<String>();
client.Connect();
ListDirectory(client, ".", ref files);
client.Disconnect();
files.Dump();
}
}
void ListDirectory(SftpClient client, String dirName, ref List<String> files)
{
foreach (var entry in client.ListDirectory(dirName))
{
if (entry.IsDirectory)
{
ListDirectory(client, entry.FullName, ref files);
}
else
{
files.Add(entry.FullName);
}
}
}
试试这个:
var filePaths = client.ListDirectory(client.WorkingDirectory);
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using Renci.SshNet;
using Renci.SshNet.Sftp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SFTPFileMonitor
{
public class GetListOfFiles
{
[FunctionName("GetListOfFiles")]
public async Task<IActionResult> RunAsync([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
List<SftpFile> zFiles;
int fileCount;
decimal totalSizeGB;
long totalSizeBytes;
using (SftpClient sftpClient = new SftpClient("hostname", "username", "password"))
{
sftpClient.Connect();
zFiles = await GetFiles(sftpClient, sftpClient.WorkingDirectory, new List<SftpFile>());
fileCount = zFiles.Count;
totalSizeBytes = zFiles.Sum(l => l.Length);
totalSizeGB = BytesToGB(totalSizeBytes);
}
return new OkObjectResult(new { fileCount, totalSizeBytes, totalSizeGB, zFiles });
}
private async Task<List<SftpFile>> GetFiles(SftpClient sftpClient, string directory, List<SftpFile> files)
{
foreach (SftpFile sftpFile in sftpClient.ListDirectory(directory))
{
if (sftpFile.Name.StartsWith('.')) { continue; }
if (sftpFile.IsDirectory)
{
await GetFiles(sftpClient, sftpFile.FullName, files);
}
else
{
files.Add(sftpFile);
}
}
return files;
}
private decimal BytesToGB(long bytes)
{
return Convert.ToDecimal(bytes) / 1024 / 1024 / 1024;
}
}
}
public class TransportResponse
{
public string directoryName { get; set; }
public string fileName { get; set; }
public DateTime fileTimeStamp { get; set; }
public MemoryStream fileStream { get; set; }
public List<TransportResponse> lstTransportResponse { get; set; }
}
List<TransportResponse> lstResponse = new List<TransportResponse>();
using (var client = new SftpClient(connectionInfo))
{
try
{
Console.WriteLine("Connecting to " + connectionInfo.Host + " ...");
client.Connect();
Console.WriteLine("Connected to " + connectionInfo.Host + " ...");
}
catch (Exception ex)
{
Console.WriteLine("Could not connect to "+ connectionInfo.Host +" server. Exception Details: " + ex.Message);
}
if (client.IsConnected)
{
var files = client.ListDirectory(transport.SourceFolder);
lstResponse = downloadFilesInDirectory(files, client);
client.Disconnect();
}
else
{
Console.WriteLine("Could not download files from "+ transport.TransportIdentifier +" because client was not connected.");
}
}
private static List<TransportResponse> downloadFilesInDirectory(IEnumerable<SftpFile> files, SftpClient client)
{
List<TransportResponse> lstResponse = new List<TransportResponse>();
foreach (var file in files)
{
if (!file.IsDirectory)
{
if (file.Name != "." && file.Name != "..")
{
if (!TransportDAL.checkFileExists(file.Name, file.LastWriteTime))
{
using (MemoryStream fs = new MemoryStream())
{
try
{
Console.WriteLine("Reading " + file.Name + "...");
client.DownloadFile(file.FullName, fs);
fs.Seek(0, SeekOrigin.Begin);
lstResponse.Add(new TransportResponse { fileName = file.Name, fileTimeStamp = file.LastWriteTime, fileStream = new MemoryStream(fs.GetBuffer()) });
}
catch(Exception ex)
{
Console.WriteLine("Error reading File. Exception Details: " + ex.Message);
}
}
}
else
{
Console.WriteLine("File was downloaded previously");
}
}
}
else
{
if (file.Name != "." && file.Name != "..")
{
lstResponse.Add(new TransportResponse { directoryName = file.Name,lstTransportResponse = downloadFilesInDirectory(client.ListDirectory(file.Name), client) });
}
}
}
return lstResponse;
}
@Carlos Bos
这个库有一些怪癖,使得递归列表变得棘手,因为ChangeDirectory和ListDirectory之间的交互并不像你期望的那样工作。
正确的
当ChangeDirectory()参数是“.”时,它可以正常工作。
但是如果你执行
SftpClient sftp ...;
sftp.ChangeDirectory("some_folder");
//get file list
List<SftpFile> fileList = sftp.ListDirectory("some_folder").ToList();
然后会出现断言,因为ListDirectory()调用期望的是"some_folder/some_folder"
我使用的解决方法是在远程上传/重命名到"some_folder"之前保存和恢复当前目录,并且在操作之前需要列出该文件夹(例如查看文件是否已存在)
string working_directory = sftp.WorkingDirectory;
sftp.ChangeDirectory("some_folder");
sftp.RenameFile("name", "new_name");
sftp.ChangeDirectory(working_directory);
要检查文件是否存在,这个调用就足够了
sftp.Exists(path)
或者如果您想添加其他条件,例如区分大小写与否
public FileExistence checkFileExists(string folder, string fileName)
{
//get file list
List<SftpFile> fileList = sftp.ListDirectory(folder).ToList();
if (fileList == null)
{
return FileExistence.UNCONFIRMED;
}
foreach (SftpFile f in fileList)
{
Console.WriteLine(f.ToString());
//a not case sensitive comparison is made
if (f.IsRegularFile && f.Name.ToLower() == fileName.ToLower())
{
return FileExistence.EXISTS;
}
}
//if not found in traversal , it does not exist
return FileExistence.DOES_NOT_EXIST;
}
FileExistence是什么
public enum FileExistence
{
EXISTS,
DOES_NOT_EXIST,
UNCONFIRMED
};
扩展方法:
public static IEnumerable<SftpFile> ListDirectoryRecursive(this SftpClient client, String dirName)
{
foreach (var entry in client.ListDirectory(dirName))
{
if (Regex.IsMatch(entry.Name, "^\\.+$")) continue;
yield return entry;
if (entry.IsDirectory)
foreach (var innerEntry in ListDirectoryRecursive(client, entry.FullName))
yield return innerEntry;
}
}