Reputation: 433
I am using Renci.SshNet.Sftp
to make a connection to an SFTP server. Once connected, I am returning an SftpFileStream
of a file found on the server to an action of my controller. The controller actually sends a reader of the SftpFileStream
to a service method which parses the stream and does what it needs to with the information.
Here is the question...
When instantiating a new SftpClient
, one should use the using
keyword to ensure that the connection is disposed of. However, if I do that, then the SftpClient
gets disposed before the file can be parsed. To fix this, I remove the using
statement, and all is well. But then I have no way of disposing the SftpClient
. So what to do?
Here is some code...
Connection Service:
public class SFTPConnectionService
{
public SftpFileStream GetRemoteFile(string filename)
{
// Server credentials
var host = "host";
var port = 22;
var username = "username";
var password = "password";
var sftp = new SftpClient(host, port, username, password);
// Connect to the SFTP server
sftp.Connect();
// Read the file in question
var file = sftp.OpenRead(filename);
return file;
}
}
Controller:
public class RemoteFileController : Controller
{
public ActionResult SFTP()
{
var sftpService = new SFTPConnectionService();
using (var reader = new StreamReader(sftpService.GetRemoteFile(@"path/filename.txt")))
{
// Do whatever with the file
return View("View", ViewModel);
}
}
}
So I am using a using
block around the StreamReader
that reads the file, so that will be disposed of, but that still leaves the SftpClient
that needs to be disposed of.
I had thought about copying the SftpFileStream
to another stream, and returning that so that I could wrap the SftpClient
in a using
block, but that seems like a bad idea already, and I also don't know how big the file is, so I really want to stream it from the SFTP server instead of copying it all into memory.
How do I handle this situation?
Upvotes: 1
Views: 2323
Reputation: 152566
I really want to stream it from the SFTP server instead of copying it all into memory
Well it's all going to get into memory anyway when it gets added to the ViewModel. Here's a few options:
IDisposable
and is responsible for disposing of the stream and the client.If you want to make SFTPConnectionService
implement IDisposable
then you would do something like this:
SFTPConnectionService
and StreamReader
Dispose
method to SFTPConnectionService
that calls Dispose
on each of themSomething like this:
public class SFTPConnectionService : IDisposable
{
private SftpClient sftp;
private SftpFileStream file;
public SftpFileStream GetRemoteFile(string filename)
{
// Server credentials
var host = "host";
var port = 22;
var username = "username";
var password = "password";
sftp = new SftpClient(host, port, username, password);
// Connect to the SFTP server
sftp.Connect();
// Read the file in question
file = sftp.OpenRead(filename);
return file;
}
// Recommended implementation of IDisposable:
public void Dispose()
{
Dispose(true);
// Use SupressFinalize in case a subclass
// of this type implements a finalizer.
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if(sftp != null)
sftp.Dispose();
if(file != null)
file.Dispose();
}
// Indicate that the instance has been disposed.
_disposed = true;
}
}
}
your calling code would then be
using(var sftpService = new SFTPConnectionService())
using(var reader = new StreamReader(sftpService.GetRemoteFile(@"path/filename.txt")))
{
// Do whatever with the file
return View("View", ViewModel);
}
Upvotes: 1