Streaming a file from SharpSSH
Asked Answered
F

1

6

I'm trying to use SharpSSH to get a file from a remote SFTP server, and I want to read it out as a stream.

I found:

  • class Sftp, which has a Get method that saves it to a local file -- close

  • class SshStream, which might do kind of what I want, but seems disjoint from Sftp so I might have to implement the SFTP part myself (??)

  • class ChannelSftp, which implements SFTP methods like get(String, OutputStream), which seems perfect, except it's a low-level class and it's not at all obvious to me how to even instantiate it

It looks like if Sftp's ChannelSftp SftpChannel property wasn't private, I could use that and everything would be perfect. I'd like to avoid hacking up SharpSSH if possible, though.

Am I missing something?

Flyblow answered 21/7, 2010 at 0:54 Comment(0)
B
8

I worked something out, and tested it out. Give it a try, and feel free to massage the API.

First off, you will need to surface up a method that allows you to take advantage of the ChannelSftp methods that call for OutputStreams instead of destination file names. If you don't want to use reflection to do it, then add this method to the Sftp class and recompile SharpSSH.

public void GetWithStream(string fromFilePath, Tamir.SharpSsh.java.io.OutputStream stream)
{
    cancelled = false;
    SftpChannel.get(fromFilePath, stream, m_monitor);
}

Next, create a wrapper for the Stream class that is compatible with Tamir.SharpSsh.java.io.OutputStream, such as the one below:

using System.IO;
using Tamir.SharpSsh.java.io;

public class GenericSftpOutputStream : OutputStream
{
    Stream stream;
    public GenericSftpOutputStream(Stream stream)
    {
        this.stream = stream;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        stream.Write(buffer, offset, count);
    }

    public override void Flush()
    {
        stream.Flush();
    }

    public override void Close()
    {
        stream.Close();
    }

    public override bool CanSeek
    {
        get { return stream.CanSeek; }
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return stream.Seek(offset, origin);
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);

        if (this.stream != null)
        {
            this.stream.Dispose();
            this.stream = null;
        }
    }
}

With those ingredients, you can now use OpenSSH to stream its data to the stream of your choice, as demonstrated below with a FileStream.

using System.IO; using Tamir.SharpSsh;

class Program
{
    static void Main(string[] args)
    {
        var host = "hostname";
        var user = "username";
        var pass = "password";
        var file = "/some/remote/path.txt";
        var saveas = @"C:\some\local\path";

        var client = new Sftp(host, user, pass);
        client.Connect();

        using (var target = new GenericSftpOutputStream(File.Open(saveas, FileMode.OpenOrCreate)))
        {
            client.GetWithStream(file, target);
        }

        client.Close();
    }
}
Bismuthinite answered 31/7, 2010 at 3:49 Comment(10)
Wow. I haven't tried it yet -- I was hoping for an API call I'd missed -- but this looks like it could be (part of) what I need to do. For now I'm going to just Get() to a file, but maybe later I'll switch to this. :-)Flyblow
I know this is an old question, but I'm adding on some code to SharpSSH and streamlining bits, I'm going to try and patch this in at bitbucket.org/mattgwagner/sharpssh . Thanks!!Forewoman
@Matt - Awesome! Thanks for the heads-up and good-on-you for contributing.Bismuthinite
I get the following error Could not find a part of the path 'C:\Folder\'. How can i solve this ?Trustless
@Trustless File.Open is intolerant if the specified path refers to a folder that does not exist or is inaccessible due to permissions. Very likely, you simply need to create the folder first. Also, it has been two years since this posting. Take a look at SharpSSH to see if they have updated their API to be stream-friendly. If they have, it could save you some work.Bismuthinite
Does this stream support seeking? Can I download just bits of a file with this?Flaccid
@Matthias - Not certain, though often when you wrap a stream with another in this fashion, the outer stream supports whatever the underlying stream supports. What I don't know is: if you seek to position 1000, is that position 1000 in the secured data or the unencrypted data. Luckily, you can certainly try it out without much effort.Bismuthinite
Would it be possible to use this to generate a byte array?Hallowell
@grin0048, yes sir. Now, it has been nearly four years since I posted this answer, so I cannot vouch for whether it is necessary with the current version of SharpSSH; however, if you consider the Program class in my example, you could use a MemoryStream instead of the File.Open() call, effectively letting you write to an in-memory stream instead of a file. You can later fetch the MemoryStream's contents as a byte array. Good luck!Bismuthinite
Thank you. I did manage to figure it out eventually. This answer is still quite relevant as it doesn't seem like the library has changed much since.Hallowell

© 2022 - 2024 — McMap. All rights reserved.