问题
I am using SSH.NET library to create SFTP client. I need to resume the download, if network connection becomes available again within this timeout. I am using the below mentioned approach as shown in many examples.
PrivateKeyFile ObjPrivateKey = new PrivateKeyFile(keyStream);
PrivateKeyAuthenticationMethod ObjPrivateKeyAutentication = new PrivateKeyAuthenticationMethod(username, ObjPrivateKey);
var connectionInfo = new ConnectionInfo(hostAddress, port, username, ObjPrivateKeyAutentication);
try
{
using (var client = new SftpClient(connectionInfo))
{
client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(10);
client.Connect();
if (!client.IsConnected)
{
return false;
}
if (!client.Exists(source))
{
return false;
}
var fileName = Path.GetFileName(source);
using (var fs = new FileStream(destination + fileName, FileMode.Create))
{
client.DownloadFile(source, fs, printActionDel);
fs.Close();
returnState = true;
}
client.Disconnect();
client.Dispose();
}
}
I am unplugging the network cable to interrupt the download and test the timeout scenario. Though I enable the internet connection again within the timeout to resume the download, it is not resuming. What am I doing wrong here? Please advice.
回答1:
If you expect the SSH.NET to reconnect within SftpClient.DownloadFile
, it won't.
You have to implement the re-connect and transfer resume on your own.
PrivateKeyFile ObjPrivateKey = new PrivateKeyFile(keyStream);
PrivateKeyAuthenticationMethod ObjPrivateKeyAutentication =
new PrivateKeyAuthenticationMethod(username, ObjPrivateKey);
var connectionInfo =
new ConnectionInfo(hostAddress, port, username, ObjPrivateKeyAutentication);
bool retry = false;
do
{
bool retrying = retry;
retry = false;
using (var client = new SftpClient(connectionInfo))
{
client.Connect();
if (!client.Exists(source))
{
return false;
}
var fileName = Path.GetFileName(source);
var destinationFile = Path.Combine(destination, fileName);
try
{
Stream destinationStream;
if (retrying)
{
destinationStream = new FileStream(destinationFile, FileMode.Append);
}
else
{
destinationStream = new FileStream(destinationFile, FileMode.Create);
}
using (destinationStream)
using (var sourceStream = client.Open(source, FileMode.Open))
{
sourceStream.Seek(destinationStream.Length, SeekOrigin.Begin);
// You can simply use sourceStream.CopyTo(destinationStream) here.
// But if you need to monitor download progress,
// you have to loop yourself.
byte[] buffer = new byte[81920];
int read;
ulong total = (ulong)destinationStream.Length;
while ((read = sourceStream.Read(buffer, 0, buffer.Length)) != 0)
{
destinationStream.Write(buffer, 0, read);
total = total + (ulong)read;
// report progress
printActionDel(total);
}
}
}
catch (SshException e)
{
retry = true;
}
}
}
while (retry);
Or use another SFTP library that supports the resume natively.
For example WinSCP .NET assembly does resume automatically in its Session.GetFiles method.
SessionOptions sessionOptions = new SessionOptions
{
Protocol = Protocol.Sftp,
HostName = hostAddress,
PortNumber = port,
UserName = username,
SshHostKeyFingerprint = "ssh-dss 2048 0b:77:8b:68:f4:45:b1:3c:87:ad:5c:be:3b:c5:72:78",
SshPrivateKeyPath = keyPath
};
using (Session session = new Session())
{
session.FileTransferProgress += session_FileTransferProgress;
session.Open(sessionOptions);
var fileName = Path.GetFileName(source);
var destinationFile = Path.Combine(destination, fileName);
session.GetFiles(source, destinationFile).Check();
}
WinSCP GUI can generate an SFTP download code template like the one above for you.
(I'm the author of WinSCP)
来源:https://stackoverflow.com/questions/44139122/automatically-resume-sftp-download-after-network-connection-is-restored-with-ssh