private bool UploadDirectory(string fullPath, SCPChannelStream stream, bool preserveTime, Cancellation cancellation, SCPFileTransferProgressDelegate progressDelegate) { Debug.Assert(fullPath != null); if (cancellation != null && cancellation.IsRequested) { return false; // cancel } string directoryName = Path.GetFileName(fullPath); if (directoryName != null) { // not a root directory if (progressDelegate != null) progressDelegate(fullPath, directoryName, SCPFileTransferStatus.CreateDirectory, 0, 0); if (preserveTime) { SendModTime( stream, Directory.GetLastWriteTimeUtc(fullPath), Directory.GetLastAccessTimeUtc(fullPath) ); } string line = new StringBuilder() .Append('D') .Append(GetPermissionsText(true)) .Append(" 0 ") .Append(directoryName) .Append('\n') .ToString(); stream.Write(_encoding.GetBytes(line)); CheckResponse(stream); if (progressDelegate != null) progressDelegate(fullPath, directoryName, SCPFileTransferStatus.DirectoryCreated, 0, 0); } foreach (String fullSubDirPath in Directory.GetDirectories(fullPath)) { bool continued = UploadDirectory(fullSubDirPath, stream, preserveTime, cancellation, progressDelegate); if (!continued) return false; // cancel } foreach (String fullFilePath in Directory.GetFiles(fullPath)) { bool continued = UploadFile(fullFilePath, stream, preserveTime, cancellation, progressDelegate); if (!continued) return false; // cancel } if (directoryName != null) { // not a root directory string line = "E\n"; stream.Write(_encoding.GetBytes(line)); CheckResponse(stream); } return true; // continue }
private bool UploadFile(string fullPath, SCPChannelStream stream, bool preserveTime, Cancellation cancellation, SCPFileTransferProgressDelegate progressDelegate) { Debug.Assert(fullPath != null); string fileName = Path.GetFileName(fullPath); FileInfo fileInfo = new FileInfo(fullPath); long fileSize = fileInfo.Length; ulong transmitted = 0; if (progressDelegate != null) progressDelegate(fullPath, fileName, SCPFileTransferStatus.Open, (ulong)fileSize, transmitted); if (preserveTime) { SendModTime( stream, File.GetLastWriteTimeUtc(fullPath), File.GetLastAccessTimeUtc(fullPath) ); } using (FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { string line = new StringBuilder() .Append('C') .Append(GetPermissionsText(false)) .Append(' ') .Append(fileSize.ToString(NumberFormatInfo.InvariantInfo)) .Append(' ') .Append(fileName) .Append('\n') .ToString(); stream.Write(_encoding.GetBytes(line)); CheckResponse(stream); byte[] buff = new byte[stream.GetPreferredDatagramSize()]; long remain = fileSize; while (remain > 0) { if (cancellation != null && cancellation.IsRequested) { if (progressDelegate != null) progressDelegate(fullPath, fileName, SCPFileTransferStatus.CompletedAbort, (ulong)fileSize, transmitted); return false; // cancel } int readLength = fileStream.Read(buff, 0, buff.Length); if (readLength <= 0) break; if (readLength > remain) readLength = (int)remain; stream.Write(buff, readLength); remain -= readLength; transmitted += (ulong)readLength; if (progressDelegate != null) progressDelegate(fullPath, fileName, SCPFileTransferStatus.Transmitting, (ulong)fileSize, transmitted); } stream.Write(ZERO); CheckResponse(stream); } if (progressDelegate != null) progressDelegate(fullPath, fileName, SCPFileTransferStatus.CompletedSuccess, (ulong)fileSize, transmitted); return true; }
private bool CreateDirectory(SCPChannelStream stream, string directoryPath, SCPModTime modTime, Cancellation cancellation, SCPFileTransferProgressDelegate progressDelegate) { if (cancellation != null && cancellation.IsRequested) { return false; // cancel } string directoryName = Path.GetFileName(directoryPath); if (progressDelegate != null) progressDelegate(directoryPath, directoryName, SCPFileTransferStatus.CreateDirectory, 0, 0); if (!Directory.Exists(directoryPath)) { // skip if already exists try { Directory.CreateDirectory(directoryPath); } catch (Exception e) { SendError(stream, "failed to create a directory"); throw new SCPClientException("Failed to create a directory: " + directoryPath, e); } } if (modTime != null) { try { Directory.SetLastWriteTimeUtc(directoryPath, modTime.MTime); Directory.SetLastAccessTimeUtc(directoryPath, modTime.ATime); } catch (Exception e) { SendError(stream, "failed to modify time of a directory"); throw new SCPClientException("Failed to modify time of a directory: " + directoryPath, e); } } stream.Write(ZERO); if (progressDelegate != null) progressDelegate(directoryPath, directoryName, SCPFileTransferStatus.DirectoryCreated, 0, 0); return true; }
private bool CreateFile(SCPChannelStream stream, string filePath, SCPEntry entry, SCPModTime modTime, Cancellation cancellation, SCPFileTransferProgressDelegate progressDelegate) { string fileName = Path.GetFileName(filePath); ulong transmitted = 0; if (progressDelegate != null) progressDelegate(filePath, fileName, SCPFileTransferStatus.Open, (ulong)entry.FileSize, transmitted); FileStream fileStream; try { fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read); } catch (Exception e) { SendError(stream, "failed to create a file"); throw new SCPClientException("Failed to create a file: " + filePath, e); } stream.Write(ZERO); using (fileStream) { byte[] buff = new byte[FILE_TRANSFER_BLOCK_SIZE]; long remain = entry.FileSize; try { while (remain > 0) { if (cancellation != null && cancellation.IsRequested) { if (progressDelegate != null) progressDelegate(filePath, fileName, SCPFileTransferStatus.CompletedAbort, (ulong)entry.FileSize, transmitted); return false; // cancel } int maxLength = (int)Math.Min((long)buff.Length, remain); int readLength = stream.Read(buff, maxLength, _protocolTimeout); fileStream.Write(buff, 0, readLength); remain -= readLength; transmitted += (ulong)readLength; if (progressDelegate != null) progressDelegate(filePath, fileName, SCPFileTransferStatus.Transmitting, (ulong)entry.FileSize, transmitted); } } catch (Exception e) { SendError(stream, "failed to write to a file"); throw new SCPClientException("Failed to write to a file: " + filePath, e); } } if (modTime != null) { try { File.SetLastWriteTimeUtc(filePath, modTime.MTime); File.SetLastAccessTimeUtc(filePath, modTime.ATime); } catch (Exception e) { SendError(stream, "failed to modify time of a file"); throw new SCPClientException("Failed to modify time of a file: " + filePath, e); } } CheckResponse(stream); stream.Write(ZERO); if (progressDelegate != null) progressDelegate(filePath, fileName, SCPFileTransferStatus.CompletedSuccess, (ulong)entry.FileSize, transmitted); return true; }
/// <summary> /// Download files or directories. /// </summary> /// <remarks> /// <para>Unfortunately, Granados sends a command line in the ASCII encoding. /// So the "remotePath" must be an ASCII text.</para> /// </remarks> /// <param name="remotePath">Remote path (Unix path)</param> /// <param name="localPath">Local path (Windows' path)</param> /// <param name="recursive">Specifies recursive mode</param> /// <param name="preserveTime">Specifies to preserve time of the directory or file.</param> /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param> /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param> public void Download(string remotePath, string localPath, bool recursive, bool preserveTime, Cancellation cancellation, SCPFileTransferProgressDelegate progressDelegate) { if (!IsAscii(remotePath)) throw new SCPClientException("Remote path must consist of ASCII characters."); string absLocalPath = Path.GetFullPath(localPath); string command = "scp -f "; if (recursive) command += "-r "; if (preserveTime) command += "-p "; command += EscapeUnixPath(remotePath); string localBasePath = null; // local directory to store SCPModTime modTime = null; Stack<string> localBasePathStack = new Stack<string>(); using (SCPChannelStream stream = new SCPChannelStream()) { stream.Open(_connection, command, _protocolTimeout); stream.Write(ZERO); while (true) { byte[] lineBytes = stream.ReadUntil(LF, _protocolTimeout); if (lineBytes[0] == 1 || lineBytes[0] == 2) { // Warning or Error string message = _encoding.GetString(lineBytes, 1, lineBytes.Length - 2); throw new SCPClientException(message); } if (lineBytes[0] == 0x43 /*'C'*/ || lineBytes[0] == 0x44 /*'D'*/) { SCPEntry entry; try { entry = ParseEntry(lineBytes); } catch (Exception e) { SendError(stream, e.Message); throw; } if (entry.IsDirectory) { string directoryPath = DeterminePathToCreate(localBasePath, absLocalPath, entry); bool continued = CreateDirectory(stream, directoryPath, modTime, cancellation, progressDelegate); if (!continued) break; modTime = null; localBasePathStack.Push(localBasePath); localBasePath = directoryPath; } else { string filePath = DeterminePathToCreate(localBasePath, absLocalPath, entry); bool continued = CreateFile(stream, filePath, entry, modTime, cancellation, progressDelegate); if (!continued) break; modTime = null; if (!recursive) break; } } else if (lineBytes[0] == 0x54 /*'T'*/) { if (preserveTime) { try { modTime = ParseModTime(lineBytes); } catch (Exception e) { SendError(stream, e.Message); throw; } } stream.Write(ZERO); } else if (lineBytes[0] == 0x45 /*'E'*/) { if (localBasePathStack.Count > 0) { localBasePath = localBasePathStack.Pop(); if (localBasePath == null) break; } stream.Write(ZERO); } else { SendError(stream, "Invalid control"); throw new SCPClientException("Invalid control"); } } } }
/// <summary> /// Upload files or directories. /// </summary> /// <remarks> /// <para>Unfortunately, Granados sends a command line in the ASCII encoding. /// So the "remotePath" must be an ASCII text.</para> /// </remarks> /// <param name="localPath">Local path (Windows' path)</param> /// <param name="remotePath">Remote path (Unix path)</param> /// <param name="recursive">Specifies recursive mode</param> /// <param name="preserveTime">Specifies to preserve time of the directory or file.</param> /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param> /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param> public void Upload(string localPath, string remotePath, bool recursive, bool preserveTime, Cancellation cancellation, SCPFileTransferProgressDelegate progressDelegate) { if (!IsAscii(remotePath)) throw new SCPClientException("Remote path must consist of ASCII characters."); bool isDirectory = Directory.Exists(localPath); if (!File.Exists(localPath) && !isDirectory) throw new SCPClientException("File or directory not found: " + localPath); if (isDirectory && !recursive) throw new SCPClientException("Cannot copy directory in non-recursive mode"); string absLocalPath = Path.GetFullPath(localPath); string command = "scp -t "; if (recursive) command += "-r "; if (preserveTime) command += "-p "; command += EscapeUnixPath(remotePath); using (SCPChannelStream stream = new SCPChannelStream()) { stream.Open(_connection, command, _protocolTimeout); CheckResponse(stream); if (isDirectory) { // implies recursive mode UploadDirectory(absLocalPath, stream, preserveTime, cancellation, progressDelegate); } else { UploadFile(absLocalPath, stream, preserveTime, cancellation, progressDelegate); } } }
private void SFTPDownload(string[] remoteFiles, string localDirectoryPath) { _fileTransferCancellation = new Cancellation(); BeginSFTPThread(delegate() { ClearProgressBar(); Log("=== DOWNLOAD ==="); string localFullPath = Path.GetFullPath(localDirectoryPath); bool continued = true; try { bool overwite = false; foreach (string remotePath in remoteFiles) { continued = SFTPDownload_DownloadRecursively(remotePath, localFullPath, ref overwite); if (!continued) break; } } finally { ClearProgressBar(); } _fileTransferCancellation = null; Log("DOWNLOAD " + (continued ? "completed." : "canceled.")); }, true, true); }
private void SFTPUpload(string[] localFiles, string remoteDirectoryPath) { if (!CanExecuteSFTP()) return; _fileTransferCancellation = new Cancellation(); BeginSFTPThread(delegate() { ClearProgressBar(); Log("=== UPLOAD ==="); Log("Remote: " + remoteDirectoryPath); bool continued = true; try { bool overwite = false; foreach (string localFile in localFiles) { string localFullPath = Path.GetFullPath(localFile); continued = SFTPUpload_UploadRecursively(localFullPath, remoteDirectoryPath, ref overwite); if (!continued) break; } } finally { ClearProgressBar(); } Log("UPLOAD " + (continued ? "completed." : "canceled.")); SFTPOpenDirectory_Core(remoteDirectoryPath, true); _fileTransferCancellation = null; }, true, true); }
private void StartUpload(string[] localFiles) { Debug.Assert(localFiles != null); if (_scp == null || _scpExecuting) return; Debug.Assert(_scpThread == null || !_scpThread.IsAlive); string remotePath; if (!TryGetRemotePath(out remotePath)) return; bool recursive = this.checkRecursive.Checked; bool preseveTime = this.checkPreserveTime.Checked; _fileTransferCancellation = new Cancellation(); ChangeExecutingState(true); _scpThread = new Thread((ThreadStart) delegate() { UploadThread(localFiles, remotePath, recursive, preseveTime); ChangeExecutingState(false); _fileTransferCancellation = null; }); _scpThread.Start(); }
/// <summary> /// Upload a file. /// </summary> /// /// <param name="localPath">Local file path to upload.</param> /// <param name="remotePath">Remote file path to write.</param> /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param> /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param> /// /// <exception cref="SFTPClientErrorException">Operation failed.</exception> /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception> /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception> /// <exception cref="SFTPClientException">Error.</exception> /// <exception cref="Exception">Error.</exception> public void UploadFile(string localPath, string remotePath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate) { CheckStatus(); uint requestId = ++_requestId; ulong transmitted = 0; Exception pendingException = null; try { bool isTransferring = false; bool hasError = false; bool isCanceled = false; byte[] handle = null; try { using (FileStream fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Open, transmitted); isTransferring = true; handle = OpenFile(requestId, remotePath, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); isTransferring = false; byte[] buff = new byte[FILE_TRANSFER_BLOCK_SIZE]; while (true) { if (cancellation != null && cancellation.IsRequested) { isCanceled = true; break; } if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted); int length = fileStream.Read(buff, 0, buff.Length); if (length > 0) { isTransferring = true; WriteFile(requestId, handle, transmitted, buff, length); isTransferring = false; transmitted += (ulong)length; } if (length == 0 || length < buff.Length) break; // EOF } } } catch (Exception e) { if (isTransferring) // exception was raised in OpenFile() or WriteFile() ? throw; pendingException = e; hasError = true; } if (handle != null) { if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Close, transmitted); CloseHandle(requestId, handle); } if (progressDelegate != null) { SFTPFileTransferStatus status = hasError ? SFTPFileTransferStatus.CompletedError : isCanceled ? SFTPFileTransferStatus.CompletedAbort : SFTPFileTransferStatus.CompletedSuccess; progressDelegate(status, transmitted); } } catch (Exception) { if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted); throw; } if (pendingException != null) throw new SFTPClientException(pendingException.Message, pendingException); }
/// <summary> /// Download a file. /// </summary> /// <remarks> /// Even if download failed, local file is not deleted. /// </remarks> /// /// <param name="remotePath">Remote file path to download.</param> /// <param name="localPath">Local file path to save.</param> /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param> /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param> /// /// <exception cref="SFTPClientErrorException">Operation failed.</exception> /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception> /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception> /// <exception cref="SFTPClientException">Error.</exception> /// <exception cref="Exception">Error.</exception> public void DownloadFile(string remotePath, string localPath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate) { CheckStatus(); uint requestId = ++_requestId; ulong transmitted = 0; Exception pendingException = null; try { if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Open, transmitted); byte[] handle = OpenFile(requestId, remotePath, SSH_FXF_READ); bool isTransferring = false; bool hasError = false; bool isCanceled = false; try { using (FileStream fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { while (true) { if (cancellation != null && cancellation.IsRequested) { isCanceled = true; break; } if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted); isTransferring = true; byte[] data = ReadFile(requestId, handle, transmitted, FILE_TRANSFER_BLOCK_SIZE); isTransferring = false; if (data == null) break; // EOF if (data.Length > 0) { fileStream.Write(data, 0, data.Length); transmitted += (ulong)data.Length; } } } } catch (Exception e) { if (isTransferring) // exception was raised in ReadFile() ? throw; pendingException = e; hasError = true; } if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Close, transmitted); CloseHandle(requestId, handle); if (progressDelegate != null) { SFTPFileTransferStatus status = hasError ? SFTPFileTransferStatus.CompletedError : isCanceled ? SFTPFileTransferStatus.CompletedAbort : SFTPFileTransferStatus.CompletedSuccess; progressDelegate(status, transmitted); } } catch (Exception) { if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted); throw; } if (pendingException != null) throw new SFTPClientException(pendingException.Message, pendingException); }
/// <summary> /// Upload a file. /// </summary> /// /// <param name="localPath">Local file path to upload.</param> /// <param name="remotePath">Remote file path to write.</param> /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param> /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param> /// /// <exception cref="SFTPClientErrorException">Operation failed.</exception> /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception> /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception> /// <exception cref="SFTPClientException">Error.</exception> /// <exception cref="Exception">Error.</exception> public void UploadFile(string localPath, string remotePath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate) { CheckStatus(); uint requestId = ++_requestId; ulong transmitted = 0; Exception pendingException = null; bool hasError = false; bool dataFinished = false; byte[] handle = null; try { using (FileStream fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.Open, transmitted); } handle = OpenFile(requestId, remotePath, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); var dataToSend = new AtomicBox<DataFragment>(); var cancelTask = new CancellationTokenSource(); var cancelToken = cancelTask.Token; Task readFileTask = Task.Run(() => { // SSH_FXP_WRITE header part // 4 bytes : packet length // 1 byte : message type (SSH_FXP_WRITE) // 4 bytes : request id // 4 bytes : handle length // n bytes : handle // 8 bytes : offset // 4 bytes : length of the datagram int buffSize = _channel.MaxChannelDatagramSize - 25 - handle.Length; // use multiple buffers cyclically. // at least 3 buffers are required. DataFragment[] dataFrags = { new DataFragment(new byte[buffSize], 0, buffSize), new DataFragment(new byte[buffSize], 0, buffSize), new DataFragment(new byte[buffSize], 0, buffSize), }; int buffIndex = 0; while (true) { if (cancelToken.IsCancellationRequested) { return; } DataFragment df = dataFrags[buffIndex]; buffIndex = (buffIndex + 1) % 3; int length = fileStream.Read(df.Data, 0, df.Data.Length); if (length == 0) { df = null; // end of file } else { df.SetLength(0, length); } // pass to the sending loop while (true) { if (dataToSend.TrySet(df, 500)) { break; } if (cancelToken.IsCancellationRequested) { return; } } if (length == 0) { return; // end of file } } }, cancelToken); try { while (true) { if (cancellation != null && cancellation.IsRequested) { break; } DataFragment dataFrag = null; if (!dataToSend.TryGet(ref dataFrag, 1000)) { throw new Exception("read error"); } if (dataFrag == null) { dataFinished = true; break; } WriteFile(requestId, handle, transmitted, dataFrag.Data, dataFrag.Length); transmitted += (ulong)dataFrag.Length; if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted); } } } finally { if (!readFileTask.IsCompleted) { cancelTask.Cancel(); } readFileTask.Wait(); } } // using } catch (Exception e) { if (e is AggregateException) { pendingException = ((AggregateException)e).InnerExceptions[0]; } else { pendingException = e; } hasError = true; } try { if (handle != null) { if (progressDelegate != null) progressDelegate(SFTPFileTransferStatus.Close, transmitted); CloseHandle(requestId, handle); } if (progressDelegate != null) { SFTPFileTransferStatus status = hasError ? SFTPFileTransferStatus.CompletedError : dataFinished ? SFTPFileTransferStatus.CompletedSuccess : SFTPFileTransferStatus.CompletedAbort; progressDelegate(status, transmitted); } } catch (Exception) { if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted); } throw; } if (pendingException != null) { throw new SFTPClientException(pendingException.Message, pendingException); } }
/// <summary> /// Download a file. /// </summary> /// <remarks> /// Even if download failed, local file is not deleted. /// </remarks> /// /// <param name="remotePath">Remote file path to download.</param> /// <param name="localPath">Local file path to save.</param> /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param> /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param> /// /// <exception cref="SFTPClientErrorException">Operation failed.</exception> /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception> /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception> /// <exception cref="SFTPClientException">Error.</exception> /// <exception cref="Exception">Error.</exception> public void DownloadFile(string remotePath, string localPath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate) { CheckStatus(); uint requestId = ++_requestId; ulong transmitted = 0; Exception pendingException = null; if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.Open, transmitted); } byte[] handle; try { handle = OpenFile(requestId, remotePath, SSH_FXF_READ); } catch (Exception) { if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted); } throw; } bool hasError = false; bool dataFinished = false; try { using (FileStream fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { var dataToSave = new AtomicBox<DataFragment>(); var cancelTask = new CancellationTokenSource(); var cancelToken = cancelTask.Token; Task writeFileTask = Task.Run(() => { while (true) { DataFragment df = null; while (true) { if (dataToSave.TryGet(ref df, 500)) { break; } if (cancelToken.IsCancellationRequested) { return; } } if (df == null) { dataFinished = true; return; // end of file } fileStream.Write(df.Data, df.Offset, df.Length); } }, cancelToken); try { // fixed buffer size is used. // it is very difficult to decide optimal buffer size // because the server may change the packet size // depending on the available window size. const int buffSize = 0x10000; // use multiple buffers cyclically. // at least 3 buffers are required. DataFragment[] dataFrags = { new DataFragment(new byte[buffSize], 0, buffSize), new DataFragment(new byte[buffSize], 0, buffSize), new DataFragment(new byte[buffSize], 0, buffSize), }; int buffIndex = 0; while (true) { if (cancellation != null && cancellation.IsRequested) { break; } DataFragment df = dataFrags[buffIndex]; buffIndex = (buffIndex + 1) % 3; int length = ReadFile(requestId, handle, transmitted, buffSize, df.Data); if (length == 0) { df = null; // end of file } else { df.SetLength(0, length); } // pass to the writing task if (!dataToSave.TrySet(df, 1000)) { throw new Exception("write error"); } transmitted += (ulong)length; if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted); } if (length == 0) { writeFileTask.Wait(1000); break; // EOF } } } finally { if (!writeFileTask.IsCompleted) { cancelTask.Cancel(); } writeFileTask.Wait(); } } } catch (Exception e) { if (e is AggregateException) { pendingException = ((AggregateException)e).InnerExceptions[0]; } else { pendingException = e; } hasError = true; } try { if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.Close, transmitted); } CloseHandle(requestId, handle); if (progressDelegate != null) { SFTPFileTransferStatus status = hasError ? SFTPFileTransferStatus.CompletedError : dataFinished ? SFTPFileTransferStatus.CompletedSuccess : SFTPFileTransferStatus.CompletedAbort; progressDelegate(status, transmitted); } } catch (Exception) { if (progressDelegate != null) { progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted); } throw; } if (pendingException != null) { throw new SFTPClientException(pendingException.Message, pendingException); } }