Home > OS >  C# - Send MemoryStream from Amazon S3 bucket to a Remote Server using SFTP
C# - Send MemoryStream from Amazon S3 bucket to a Remote Server using SFTP

Time:12-20

I'm working on logic to retrieve a file from an Amazon S3 Bucket and then send it to a remote server using SFTP.

However, I'm not able to send the file because I don't have a path value - i.e., since my file is in S3, I don't have a path for it my local machine.

This is my method to send a file - it's inside a class called SftpService:

public async Task SendFile(SftpRequest request, CancellationToken cancellationToken = default)
{
    // Connect to server
    SftpClient client = new SftpClient(request.Host, request.Port, request.Username, request.Password);
    client.Connect();

    if (!client.IsConnected)
    {
        throw new SftpClientException("SFTP Connection was not complete.");
    }

    // Create an object of File Stream and pass a temp file path.
    var tempFilePath = Path.GetTempFileName();
    FileStream fileStream = new FileStream(tempFilePath, FileMode.Open);

    // Copy the MemoryStream (from S3) to the FileStream
    request.RefundFile.CopyTo(fileStream);
    fileStream.Close();

    // Upload the file. [TBD] What's the path for the file? (it is a MemoryStream)
    client.UploadFile(fileStream, tempFilePath);

    // Dispose the object by calling dispose method of sftpClient once the file has uploaded.
    client.Dispose();
}

Now, I'm trying to perform a test on this method (using NUnit).

[SetUp]
public void Setup()
{
    _sut = new SftpService(); //class
}

[Test]
public async Task SftpService_ShouldSendFileToServer_Success()
{
    var request = MockSftpRequest();

    await _sut.SendFile(request);
}

private SftpRequest MockSftpRequest()
{
    var serverMock = new ServerOptions()
    {
        BaseAddress = "195.144.107.198",
        Username = "demo",
        Password = "password",
    };

    // Create a mock MemoryStream
    var s3FileMock = new MemoryStream(Encoding.UTF8.GetBytes("This is a mock of the s3 file."));

    SftpRequest request = new SftpRequest(serverMock)
    {
        RefundFile = refundFileMock,
        Port = 22,
    };

    return request;
}

When running the test, I'm getting this error: Message: Renci.SshNet.Common.SftpPathNotFoundException : Invalid path.


So, my question is: how can I achieve to send a MemoryStream over SFTP, without a path?

CodePudding user response:

You have both parameters of SftpClient.UploadFile wrong.

  1. Your immediate issue it that you are passing local path to the second parameter of SftpClient.UploadFile. Why? Obviously one parameter must be for local file/data and the other for remote. You are already passing local data to the first argument. So SftpClient.UploadFile does not need the local path anymore. It needs the remote path.

  2. Why are you saving the [Memory]Stream to a temporary file only to open the file as another [File]Stream to pass it to SftpClient.UploadFile? Pass the MemoryStream to the SftpClient.UploadFile straight away.

client.UploadFile(request.RefundFile, "/remote/path/file");

Related question: Upload data from memory to SFTP server using SSH.NET

CodePudding user response:

[SOLUTION] After the answer from Martin Prikryl, I corrected my code to the following.

public async Task SendFile(SftpRequest request, CancellationToken cancellationToken = default)
{
    // Connect to server
    SftpClient client = new SftpClient(request.Host, request.Port, request.Username, request.Password);
    client.Connect();

    if (!client.IsConnected)
    {
        throw new SftpClientException("SFTP Connection was not complete.");
    }

    // Upload the file.
    client.UploadFile(request.RefundFile, Path.Combine(request.ServerOptions.RemoteDirectory, request.RefundFileName));

    // Dispose the object by calling dispose method of sftpClient once the file has uploaded.
    client.Dispose();
}

This way it works without issues. Thank you!

  • Related