0 votes
by (120 points)

Hello,
I am just evaluating Fileserver package to implement sftp upload/download to Aws S3. Listing S3 bucket works like a charm using virtual filesystem. But while implementing the GetContent method I got stuck. How I can connect the stream coming from Aws TransferUtility to NodeContent.CreateReadOnlyContent(stream)?
Any help would be appreciated.

All the best Gerd

Applies to: File Server

1 Answer

0 votes
by (144k points)

Hello, it looks like AWS S3 provides TransferUtility.OpenStream method, which returns a read-only stream that you can pass to NodeContent.CreateReadOnlyContent (similar to how it's done in FileServerCustomFS sample's DriveFileSystemProvider class).

However, I'm afraid that the stream returned by TransferUtility.OpenStream is not seekable, which means it doesn't actually fit SFTP protocol's requirements. But as long as SFTP clients are not actually going to use seeking when reading from the file, you might work around this by simulating a seekable stream using the PseudoSeekableStream class below.

Sample usage - get readable stream from AWS S3, wrap it by PseudoSeekableStream and pass it to NodeContent.CreateReadOnlyContent:

protected override NodeContent GetContent(NodeBase node, NodeContentParameters contentParameters)
{
    ...
    Stream awsStream = TransferUtility.OpenStream(...)
    Stream stream = new PseudoSeekableStream(awsStream);
    return NodeContent.CreateReadOnlyContent(stream);
}

PseudoSeekableStream source:

public class PseudoSeekableStream : Stream
{
    private readonly Stream _inner;
    private long _position;
    private long? _length;

    public PseudoSeekableStream(Stream inner)
    {
        _inner = inner;
    }

    public override bool CanRead
    {
        get { return _inner.CanRead; }
    }

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

    public override bool CanWrite
    {
        get { return _inner.CanWrite; }
    }

    public override long Length
    {
        get { throw new NotSupportedException(); }
    }

    public override long Position
    {
        get { return _position; }
        set { _position = value; }
    }

    public override void Flush()
    {
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (_length.HasValue)
            return 0;

        int n = _inner.Read(buffer, offset, count);
        if (n > 0)
        {
            _position += n;
        }
        else
        {
            _length = _position;
        }
        return n;
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        if (origin != SeekOrigin.Begin)
            throw new NotSupportedException();

        if (offset != _position)
        {
            if (!_length.HasValue || offset < _length)
                throw new NotSupportedException();
        }

        return _position;
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotSupportedException();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _inner.Close();
        }
    }
}
...