Renci SSH.NET:是不是可以创建一个包含不存在的子文件夹的文件夹
Posted
技术标签:
【中文标题】Renci SSH.NET:是不是可以创建一个包含不存在的子文件夹的文件夹【英文标题】:Renci SSH.NET: Is it possible to create a folder containing a subfolder that does not existRenci SSH.NET:是否可以创建一个包含不存在的子文件夹的文件夹 【发布时间】:2016-08-02 13:46:47 【问题描述】:我目前正在使用 Renci SSH.NET 使用 SFTP 将文件和文件夹上传到 Unix 服务器,并使用创建目录
sftp.CreateDirectory("//server/test/test2");
只要文件夹“test”已经存在,就可以完美运行。如果没有,CreateDirectory
方法会失败,并且每次尝试创建包含多个级别的目录时都会发生这种情况。
有没有一种优雅的方法可以递归地生成字符串中的所有目录?我假设CreateDirectory
方法会自动执行此操作。
【问题讨论】:
【参考方案1】:没有其他办法。
只需迭代目录级别,使用SftpClient.GetAttributes
测试每个级别并创建不存在的级别。
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);
【讨论】:
【参考方案2】:您好,我发现我的答案非常直接。自从我找到了这个旧帖子,我想其他人也可能会偶然发现它。公认的答案不是那么好,所以这是我的看法。它没有使用任何计数噱头,所以我认为它更容易理解。
public void CreateAllDirectories(SftpClient client, string path)
// Consistent forward slashes
path = path.Replace(@"\", "/");
foreach (string dir in path.Split('/'))
// Ignoring leading/ending/multiple slashes
if (!string.IsNullOrWhiteSpace(dir))
if(!client.Exists(dir))
client.CreateDirectory(dir);
client.ChangeDirectory(dir);
// Going back to default directory
client.ChangeDirectory("/");
【讨论】:
我的(接受的)答案更长,因为 1)它没有改变工作目录的副作用 2)它更有效,因为它不会改变工作目录(你会告诉差异,如果您的连接有很大的延迟)。 3)如果存在与您要创建的目录的名称匹配的现有文件,则您的回答不会报告错误。 我在沙发上的 SFTP 服务器上遇到问题,正斜杠被编码为 %2F【参考方案3】:对使用跨度的公认答案稍作修改。
在这种情况下可能完全没有意义,因为 sftp 客户端的开销远大于复制字符串,但它在其他类似场景中可能很有用:
public static void EnsureDirectory(this SftpClient client, string path)
if (path.Length is 0)
return;
var curIndex = 0;
var todo = path.AsSpan();
if (todo[0] == '/' || todo[0] == '\\')
todo = todo.Slice(1);
curIndex++;
while (todo.Length > 0)
var endOfNextIndex = todo.IndexOf('/');
if (endOfNextIndex < 0)
endOfNextIndex = todo.IndexOf('\\');
string current;
if (endOfNextIndex >= 0)
curIndex += endOfNextIndex + 1;
current = path.Substring(0, curIndex);
todo = path.AsSpan().Slice(curIndex);
else
current = path;
todo = ReadOnlySpan<char>.Empty;
try
client.CreateDirectory(current);
catch (SshException ex) when (ex.Message == "Already exists.")
【讨论】:
【参考方案4】:FWIW,这是我相当简单的看法。一个要求是服务器目标路径由正斜杠分隔,这是规范。我在调用函数之前检查了这一点。
private void CreateServerDirectoryIfItDoesntExist(string serverDestinationPath, SftpClient sftpClient)
if (serverDestinationPath[0] == '/')
serverDestinationPath = serverDestinationPath.Substring(1);
string[] directories = serverDestinationPath.Split('/');
for (int i = 0; i < directories.Length; i++)
string dirName = string.Join("/", directories, 0, i + 1);
if (!sftpClient.Exists(dirName))
sftpClient.CreateDirectory(dirName);
HTH
【讨论】:
【参考方案5】:Martin Prikryl 提供的代码的一点改进
不要将异常用作流控制机制。这里更好的选择是先检查当前路径是否存在。
if (client.Exists(current))
SftpFileAttributes attrs = client.GetAttributes(current);
if (!attrs.IsDirectory)
throw new Exception("not directory");
else
client.CreateDirectory(current);
而不是 try catch 构造
try
SftpFileAttributes attrs = client.GetAttributes(current);
if (!attrs.IsDirectory)
throw new Exception("not directory");
catch (SftpPathNotFoundException)
client.CreateDirectory(current);
【讨论】:
另外值得一提的是,您的代码效率较低,因为每个目录级别需要两次往返服务器。注意.Exists
是如何实现的。在内部,它与我的原始代码完全一样。它调用.GetAttributes
,如果没有抛出则返回true
。如果抛出,它会捕获SftpPathNotFoundException
并返回false
。因此,您似乎只是避免对控制流使用异常。这就是为什么我选择了一个带有单个调用和 try
... catch
构造的变体。如果服务器的延迟很大,您会发现差异。无论如何+1 :)
+ 不要将您的答案建立在现有答案的基础上。你的答案必须独立存在。所以请包括一个完整的代码,而不是说“在那个代码中使用这个而不是那个”。当然,需要确认代码的来源。以上是关于Renci SSH.NET:是不是可以创建一个包含不存在的子文件夹的文件夹的主要内容,如果未能解决你的问题,请参考以下文章
C#远程执行Linux系统中Shell命令和SFTP上传文件