使用 Renci SSH.NET 是否可以创建一个包含不存在子文件夹的文件夹

问题描述 投票:0回答:7

我目前正在使用 Renci SSH.NET 使用 SFTP 将文件和文件夹上传到 Unix 服务器,并使用创建目录

sftp.CreateDirectory("//server/test/test2");
只要文件夹

test

 已经存在,
就可以完美工作。如果没有,
CreateDirectory
方法就会失败,并且每次当您尝试创建包含多个级别的目录时都会发生这种情况。

有没有一种优雅的方法来递归生成字符串中的所有目录?我假设

CreateDirectory
方法会自动执行此操作。

c# recursion ssh sftp ssh.net
7个回答
25
投票

没有其他办法。

只需迭代目录级别,使用

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);
        }
    }
}

12
投票

对 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);
}

5
投票

嗨,我发现我的答案非常直接。自从我发现这篇旧帖子后,我想其他人也可能会偶然发现它。接受的答案不太好,所以这是我的看法。它没有使用任何计数技巧,所以我认为它更容易理解一点。

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("/");
    }

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


3
投票

我将所有答案放在一起,并提出了以下方法。

此方法不会更改工作目录,不会使用 GetAttributes() 和 Exists() 进行冗余检查(根据 Martin Prikryl,这些代码在内部是相同的),不会重复字符串。从头开始加入更容易阅读,并且也适用于相对路径。

private void CreateDirectoryRecursively(string targetPath, SftpClient client)
{
    string currentPath = "";
    if (targetPath[0] == '.')
    {
        currentPath = ".";
        targetPath = targetPath[1..];
    }
    foreach (string segment in targetPath.Split('/'))
    {
        // Ignoring leading/ending/multiple slashes
        if (!string.IsNullOrWhiteSpace(segment))
        {
            currentPath += $"/{segment}";
            if (!client.Exists(currentPath))
                client.CreateDirectory(currentPath);
        }
    }
}

0
投票

对使用跨度的接受答案进行一些修改。

在这种情况下可能完全没有意义,因为 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.") { }
            }
        }

-1
投票

我的方法更充分,更容易阅读和维护

public static void CreateDirectoryRecursively(this ISftpClient sftpClient, string path)
        {
            // Consistent forward slashes
            var separators = new char[] { Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar };
            string[] directories = path.Split(separators);

            string currentDirectory = "";

            for (int i = 1; i < directories.Length; i++)
            {
                currentDirectory = string.Join("/", currentDirectory, directories[i]);
                if (!sftpClient.Exists(currentDirectory))
                {
                    sftpClient.CreateDirectory(currentDirectory);
                }
            }
        }
© www.soinside.com 2019 - 2024. All rights reserved.