Search code examples
c#.netsshsftpssh.net

Upload to SFTP server with SSH.NET fails with SftpPathNotFoundException: 'The system cannot find the path specified.'


I'm trying to upload a file over SFTP, using Renci SSH.NET.

Connecting:

  • Connecting to the sFTP-site, using SSH.NET, seems to work: SftpClient's IsConnected returns true.

  • Connecting using FileZilla, however, triggers this warning:

Server's Host Key is Unknown

Uploading:

  1. Using SftpClient's UploadFile method:

    client.UploadFile(fileStream, "/Path/" + fileName, null);
    

    ...I get a Renci.SshNet.Common.SftpPathNotFoundException:

    The system cannot find the path specified.'

    • I've tried hardcoding the path - same result.
    • I've tried using the SftpClient's WorkingDirectory – but that translates into a string of Chinese-looking characters, and doesn't work either.
  1. Using SftpClient's BeginUploadFile method:

    client.BeginUploadFile(fileStream, "/Path/" + fileName, asyncCallback, null, UpdateUploadProgress);
    

    ...I get no errors/exceptions, but:

    • the file isn't uploaded

    • aSyncCallback and uploadCallback don't seem to work

  2. using FileZilla, I can upload just fine (using the exact same destination path as above: /Path/Filename.txt)


My code:

var connectionInfo = new ConnectionInfo(IpAddress,
            Port,
            UserName,
            new PasswordAuthenticationMethod(UserName, Password),
            new PrivateKeyAuthenticationMethod("rsa.key"));
connectionInfo.Encoding = Encoding.Unicode;
using (var client = new SftpClient(connectionInfo))
{
    client.Connect();

    if (client.IsConnected)
    {
        Console.WriteLine("SSH-client is connected");
    }
    var fileStream = new FileStream(FileMaker.GetFullyQualifiedPath(), FileMode.Open);
    client.BufferSize = 4 * 1024;
    string fileName = new FileMaker().GetFileName();
    //client.UploadFile(fileStream, "/Path/" + fileName, null);
    AsyncCallback asyncCallback = new AsyncCallback(NotifyUploadComplete);
    client.BeginUploadFile(fileStream, "/Path/" + fileName, asyncCallback, null, UpdateUploadProgress);
    client.Disconnect();
}

Callback handlers:

private void UpdateUploadProgress(ulong uploaded)
{
    MainViewModel mainViewModel = (MainViewModel)System.Windows.Application.Current.FindResource("mainViewModel");
    mainViewModel.UploadProgress = uploaded;
}

private void NotifyUploadComplete(IAsyncResult result)
{
    MessageBox.Show("File uploaded.", "File uploaded");
}

I made a minimal example and tested it (same result).

Using UploadFile:

var connectionInfo = new ConnectionInfo(IpAddress,
            Port,
            UserName,
            new PasswordAuthenticationMethod(UserName, Password),
            new PrivateKeyAuthenticationMethod("rsa.key"));
connectionInfo.Encoding = Encoding.Unicode;
using (var client = new SftpClient(connectionInfo))
{
    client.Connect();

    if (client.IsConnected)
    {
        Console.WriteLine("SSH-client is connected");
    }
    var fileStream = new FileStream(@"C:\File.txt", FileMode.Open);
    client.BufferSize = 4 * 1024;
    client.UploadFile(fileStream, "/SSHUsersPath/File.txt", null);
    //AsyncCallback asyncCallback = new AsyncCallback(NotifyUploadComplete);
    //client.BeginUploadFile(fileStream, "/SSHUsersPath/File.txt", asyncCallback, null, UpdateUploadProgress);
    client.Disconnect();
}

And here's an excerpt from the requested FileZilla log file:

2017-09-21 10:08:36 11252 1 Status: Connected to sshserv.CENSORED.com
2017-09-21 10:08:36 11252 1 Status: Retrieving directory listing...
2017-09-21 10:08:36 11252 1 Command: pwd
2017-09-21 10:08:36 11252 1 Response: Current directory is: "/SSHUsersPath"
2017-09-21 10:08:36 11252 1 Command: ls
2017-09-21 10:08:37 11252 1 Status: Listing directory /SSHUsersPath
2017-09-21 10:08:37 11252 1 Status: Directory listing of "/SSHUsersPath" successful
2017-09-21 10:08:58 11252 3 Status: Connecting to sshserv.CENSORED.com...
2017-09-21 10:08:58 11252 3 Response: fzSftp started, protocol_version=8
2017-09-21 10:08:58 11252 3 Command: open "[email protected]" 22
2017-09-21 10:08:59 11252 3 Command: Trust new Hostkey: Once
2017-09-21 10:09:00 11252 3 Command: Pass: *****
2017-09-21 10:09:00 11252 3 Status: Connected to sshserv.CENSORED.com
2017-09-21 10:09:00 11252 3 Status: Starting upload of C:\File.txt
2017-09-21 10:09:00 11252 3 Command: cd "/SSHUsersPath"
2017-09-21 10:09:01 11252 3 Response: New directory is: "/SSHUsersPath"
2017-09-21 10:09:01 11252 3 Command: put "C:\File.txt" "File.txt"
2017-09-21 10:09:01 11252 3 Command: local:C:\File.txt => remote:/SSHUsersPath/File.txt
2017-09-21 10:09:01 11252 3 Status: File transfer successful, transferred 4 bytes in 1 second
2017-09-21 10:09:01 11252 3 Status: Retrieving directory listing of "/SSHUsersPath"...
2017-09-21 10:09:01 11252 3 Command: ls
2017-09-21 10:09:01 11252 3 Status: Listing directory /SSHUsersPath
2017-09-21 10:09:02 11252 3 Status: Directory listing of "/SSHUsersPath" successful
2017-09-21 10:09:17 11252 1 Status: Deleting "/SSHUsersPath/File.txt"
2017-09-21 10:09:17 11252 1 Command: rm "/SSHUsersPath/File.txt"
2017-09-21 10:09:18 11252 1 Response: rm /SSHUsersPath/File.txt: OK

Solution

  • The primary problem is this:

    connectionInfo.Encoding = Encoding.Unicode;
    

    The Encoding.Unicode is UTF-16. No SFTP server will ever handle UTF-16 encoded file names. SFTP server should use UTF-8. For that reason SSH.NET defaults Encoding.UTF8. If with UTF-8 the server corrupts non-ascii characters in filenames (the .Encoding is about file names, not file contents), it must be because the SFTP is broken and uses some legacy encoding. But for sure, it's not UTF-16. It rather some ISO* encoding or Windows-* encoding.


    The other problem is that, when you use BeginUploadFile, you do not wait for it to finish and immediately disconnect.