diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs index 1eaccf83e..c3697650a 100644 --- a/src/Renci.SshNet/ServiceFactory.cs +++ b/src/Renci.SshNet/ServiceFactory.cs @@ -164,6 +164,8 @@ public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSe { var fileAttributes = sftpSession.EndLStat(statAsyncResult); fileSize = fileAttributes.Size; + + // calculate maxPendingReads based on remaining size, not total filesize (needed for resume support) maxPendingReads = Math.Min(10, (int) Math.Ceiling((double)(fileSize - (long)offset) / chunkSize) + 1); } catch (SshException ex) diff --git a/src/Renci.SshNet/Sftp/SftpFileReader.cs b/src/Renci.SshNet/Sftp/SftpFileReader.cs index f288e6c0a..2c535613e 100644 --- a/src/Renci.SshNet/Sftp/SftpFileReader.cs +++ b/src/Renci.SshNet/Sftp/SftpFileReader.cs @@ -63,8 +63,6 @@ public SftpFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, i _sftpSession = sftpSession; _chunkSize = chunkSize; _fileSize = fileSize; - _offset = offset; - _readAheadOffset = offset; _semaphore = new SemaphoreLight(maxPendingReads); _queue = new Dictionary(maxPendingReads); _readLock = new object(); @@ -72,6 +70,11 @@ public SftpFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, i _disposingWaitHandle = new ManualResetEvent(initialState: false); _waitHandles = _sftpSession.CreateWaitHandleArray(_disposingWaitHandle, _semaphore.AvailableWaitHandle); + // When resuming a download (offset > 0), set the initial offset of the remote file to + // the same offset as the local output file. Read-ahead also starts at the same offset. + _offset = offset; + _readAheadOffset = offset; + StartReadAhead(); } diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs index 01134b854..887b07fde 100644 --- a/src/Renci.SshNet/SftpClient.cs +++ b/src/Renci.SshNet/SftpClient.cs @@ -980,6 +980,7 @@ public void UploadFile(Stream input, string path, bool canOverride, Action 0) { + // if the local stream position is not zero, open the remote file in APPEND mode to resume upload flags = Flags.Write | Flags.Append; } else if (canOverride) @@ -1122,6 +1123,7 @@ public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, if (input.Position > 0) { + // if the local stream position is not zero, open the remote file in APPEND mode to resume upload flags = Flags.Write | Flags.Append; } else if (canOverride) @@ -2432,6 +2434,7 @@ private void InternalUploadFile(Stream input, string path, Flags flags, SftpUplo var handle = _sftpSession.RequestOpen(fullPath, flags); + // Set the initial offset of the remote file to the same as the local file to allow resuming var offset = (ulong)input.Position; // create buffer of optimal length