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上传文件

在SSIS中使用Renci时如何避免崩溃

使用Renci.SshNet实现sftp文件上传和下载

使用Renci.SshNet实现sftp文件上传和下载

如何在 X++(或 C#)中使用 Renci.SshNet 从其他服务器安全地发送文件,你使用端口转发吗?

SSH.NET > 用于端口转发的 SSH 客户端