Renci SSH.NET: Is it possible to create a folder containing a subfolder that does not exist

14,359

Solution 1

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

Solution 2

A little improvement on the code provided by Martin Prikryl

Don't use Exceptions as a flow control mechanism. The better alternative here is to check if the current path exists first.

if (client.Exists(current))
{
    SftpFileAttributes attrs = client.GetAttributes(current);
    if (!attrs.IsDirectory)
    {
        throw new Exception("not directory");
    }
}
else
{
    client.CreateDirectory(current);
}

instead of the try catch construct

try
{
    SftpFileAttributes attrs = client.GetAttributes(current);
    if (!attrs.IsDirectory)
    {
        throw new Exception("not directory");
    }
}
catch (SftpPathNotFoundException)
{
    client.CreateDirectory(current);
}

Solution 3

Hi I found my answer quite straight forwared. Since I found this old post, I thought others might also stumble upon it. The accepted answer is not that good, so here is my take. It does not use any counting gimmicks, so I think it's a little more easy to understand.

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

Solution 4

FWIW, here's my rather simple take on it. The one requirement is that the server destination path is seperated by forward-slashes, as is the norm. I check for this before calling the function.

    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

Share:
14,359
Erik
Author by

Erik

Updated on July 26, 2022

Comments

  • Erik
    Erik almost 2 years

    I am currently using Renci SSH.NET to upload files and folders to a Unix Server using SFTP, and creating directories using

    sftp.CreateDirectory("//server/test/test2");
    

    works perfectly, as long as the folder "test" already exists. If it doesn't, the CreateDirectory method fails, and this happens everytime when you try to create directories containing multiple levels.

    Is there an elegant way to recursively generate all the directories in a string? I was assuming that the CreateDirectory method does that automatically.

  • Martin Prikryl
    Martin Prikryl over 7 years
    Also worth saying that your code is less efficient as it requires two round trips to the server for each directory level. Note how the .Exists is implemented. Internally it does exactly what my original code do. It calls .GetAttributes and returns true if it does not throw. And it if throws, it catches the SftpPathNotFoundException and returns false. So you only seemingly avoided using exceptions for control flow. That's why I chose a variant with a single call and the try ... catch construct. If a latency to the server is big, you will tell a difference. +1 anyway :)
  • Martin Prikryl
    Martin Prikryl over 7 years
    + Do not base your answer on an exiting one. Your answer has has to stand on its own. So please include a complete code, instead of saying "use this instead of that in that code". Of course, acknowledging source of the code is expected.
  • Martin Prikryl
    Martin Prikryl over 4 years
    My (accepted) answer is longer, because 1) it does not have the side effect of changing the working directory 2) it is more efficient, as it does not change working directory (you will tell the difference, if your connection has big latency). 3) Your answer will not report an error, if there's an existing file that matches the name of the directory you want to create.
  • Tom McDonough
    Tom McDonough almost 4 years
    I get issues on my SFTP server on couchdrop, a forward slash is encoded to %2F