示例#1
0
        /// <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);
            }
        }
示例#2
0
        /// <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);
            }
        }