/// <summary> /// Retrieves a remote file from the FTP server and writes the data to a local file /// specfied in the localPath parameter. /// </summary> /// <param name="remotePath">A path and/or file name to the remote file.</param> /// <param name="localPath">A fully qualified local path to a file on the local machine.</param> /// <param name="action">The type of action to take.</param> /// <seealso cref="GetFileAsync(string, string, FileAction)" /> /// <seealso cref="PutFile(string, string, FileAction)" /> /// <seealso cref="PutFileAsync(string, string, FileAction)" /> /// <seealso cref="MoveFile" /> /// <seealso cref="FxpCopy" /> /// <seealso cref="FxpCopyAsync" /> public void GetFile(string remotePath, string localPath, FileAction action) { if (remotePath == null) throw new ArgumentNullException("remotePath"); if (remotePath.Length == 0) throw new ArgumentException("must contain a value", "remotePath"); if (localPath == null) throw new ArgumentNullException("localPath"); if (localPath.Length == 0) throw new ArgumentException("must contain a value", "localPath"); if (action == FileAction.None) throw new ArgumentOutOfRangeException("action", "must contain a value other than 'Unknown'"); localPath = CorrectLocalPath(localPath); var request = new FtpRequest(FtpCmd.Retr, remotePath); try { switch (action) { case FileAction.CreateNew: // create a file stream to stream the file locally to disk that only creates the file if it does not already exist using (Stream localFile = File.Open(localPath, FileMode.CreateNew)) { TransferData(TransferDirection.ToClient, request, localFile); } break; case FileAction.Create: // create a file stream to stream the file locally to disk using (Stream localFile = File.Open(localPath, FileMode.Create)) { TransferData(TransferDirection.ToClient, request, localFile); } break; case FileAction.CreateOrAppend: // open the local file using (Stream localFile = File.Open(localPath, FileMode.OpenOrCreate)) { // set the file position to the end so that any new data will be appended localFile.Position = localFile.Length; TransferData(TransferDirection.ToClient, request, localFile); } break; case FileAction.Resume: using (Stream localFile = File.Open(localPath, FileMode.Open)) { // get the size of the file already on the server (in bytes) long remoteSize = GetFileSize(remotePath); // if the files are the same size then there is nothing to transfer if (localFile.Length == remoteSize) return; TransferData(TransferDirection.ToClient, request, localFile, localFile.Length - 1); } break; case FileAction.ResumeOrCreate: if (File.Exists(localPath) && (new FileInfo(localPath)).Length > 0) GetFile(remotePath, localPath, FileAction.Resume); else GetFile(remotePath, localPath, FileAction.Create); break; } } catch (Exception ex) { throw new FtpException( String.Format("An unexpected exception occurred while retrieving file '{0}. Error: {1}", remotePath, ex.Message)); } }
/// <summary> /// Retrieves a remote file from the FTP server and writes the data to a local stream object /// specfied in the outStream parameter. /// </summary> /// <param name="remotePath">A path and/or file name to the remote file.</param> /// <param name="outStream">An output stream object used to stream the remote file to the local machine.</param> /// <param name="restart"> /// A true/false value to indicate if the file download needs to be restarted due to a previous /// partial download. /// </param> /// <remarks> /// If the remote path is a file name then the file is downloaded from the FTP server current working directory. /// Otherwise a fully qualified /// path for the remote file may be specified. The output stream must be writeable and can be any stream object. /// Finally, the restart parameter /// is used to send a restart command to the FTP server. The FTP server is instructed to restart the download process /// at the last position of /// of the output stream. Not all FTP servers support the restart command. If the FTP server does not support the /// restart (REST) command, /// an FtpException error is thrown. /// </remarks> /// <seealso cref="GetFileAsync(string, string, FileAction)" /> /// <seealso cref="PutFile(string, string, FileAction)" /> /// <seealso cref="PutFileAsync(string, string, FileAction)" /> /// <seealso cref="MoveFile" /> /// <seealso cref="FxpCopy" /> /// <seealso cref="FxpCopyAsync" /> public void GetFile(string remotePath, Stream outStream, bool restart) { if (remotePath == null) throw new ArgumentNullException("remotePath"); if (remotePath.Length == 0) throw new ArgumentException("must contain a value", "remotePath"); if (outStream == null) throw new ArgumentNullException("outStream"); if (outStream.CanWrite == false) throw new ArgumentException("must be writable. The CanWrite property must return the value 'true'.", "outStream"); var request = new FtpRequest(FtpCmd.Retr, remotePath); if (restart) { // get the size of the file already on the server (in bytes) long remoteSize = GetFileSize(remotePath); // if the files are the same size then there is nothing to transfer if (outStream.Length == remoteSize) return; TransferData(TransferDirection.ToClient, request, outStream, outStream.Length - 1); } else { TransferData(TransferDirection.ToClient, request, outStream); } }
private void DoIntegrityCheck(FtpRequest request, Stream stream, long restartPosition) { string path = request.Arguments[0]; long startPos = restartPosition; long endPos = stream.Length; string streamHash = ComputeChecksum(_hashAlgorithm, stream, startPos); string serverHash = GetChecksum(_hashAlgorithm, path, startPos, endPos); // string compare the dataHash to the server hash value and see if they are the same if (String.Compare(streamHash, serverHash, StringComparison.InvariantCultureIgnoreCase) != 0) throw new FtpFileIntegrityException( String.Format( "File integrity check failed. The destination integrity value '{0}' for the file '{1}' did not match the data transfer integrity value '{2}'.", serverHash, path, streamHash)); }
internal string TransferText(FtpRequest request) { Stream output = new MemoryStream(); TransferData(TransferDirection.ToClient, request, output); output.Position = 0; StreamReader reader = new StreamReader(output, Encoding.UTF8); return reader.ReadToEnd(); }
internal void TransferData(TransferDirection direction, FtpRequest request, Stream data, long restartPosition) { if (_commandConn == null || _commandConn.Connected == false) throw new FtpException("Connection is closed."); if (request == null) throw new ArgumentNullException("request", "value is required"); if (data == null) throw new ArgumentNullException("data", "value is required"); switch (direction) { case TransferDirection.ToClient: if (!data.CanWrite) throw new FtpException("Data transfer error. Data stream does not allow write operation."); break; case TransferDirection.ToServer: if (!data.CanRead) throw new FtpException("Data transfer error. Data stream does not allow read operation."); break; } // if this is a restart then the data stream must support seeking if (restartPosition > 0 && !data.CanSeek) throw new FtpDataConnectionException( "Data transfer restart error. Data conn does not allow seek operation."); try { // create a thread to begin the process of opening a data connection to the remote server OpenDataConn(); // check for a restart position if (restartPosition > 0) { // instruct the server to restart file transfer at the same position where the output stream left off SendRequest(new FtpRequest(FtpCmd.Rest, restartPosition.ToString(CultureInfo.InvariantCulture))); // set the data stream to the same position as the server data.Position = restartPosition; } // Send the data transfer command that requires a separate data connection to be established to transmit data SendRequest(request); // Wait for the data connection thread to signal back that a connection has been established WaitForDataConn(); // Test to see if the data connection failed to be established sometime the active connection fails due to security settings on the ftp server if (_dataConn == null) throw new FtpException( "Unable to establish a data connection to the destination. The destination may have refused the connection."); // Create the data stream object - handles creation of SslStream and DeflateStream objects as well Stream dataStream = GetDataStream(direction); // Based on the direction of the data transfer we need to handle the input and output streams switch (direction) { case TransferDirection.ToClient: TransferBytes(dataStream, data, _maxDownloadSpeed*1024); break; case TransferDirection.ToServer: TransferBytes(data, dataStream, _maxUploadSpeed*1024); break; } } finally { CloseDataConn(); } // If no errors occurred and this is not a quoted command then we will wait for the server to send a closing connection message WaitForHappyCodes(FtpResponseCode.ClosingDataConnection); if (_hashAlgorithm != HashingFunction.None && request.IsFileTransfer) DoIntegrityCheck(request, data, restartPosition); }
internal void TransferData(TransferDirection direction, FtpRequest request, Stream data) { TransferData(direction, request, data, 0); }
/// <summary> /// Send a FTP command request to the server. /// </summary> /// <param name="request"></param> internal void SendRequest(FtpRequest request) { if (_commandConn == null || _commandConn.Connected == false) throw new FtpException("Connection is closed."); // clear out any responses that might have been pending from a previous // failed operation DontWaitForHappyCodes(); if (ClientRequest != null) ClientRequest(this, new FtpRequestEventArgs(request)); byte[] buffer = request.GetBytes(); try { _commandStream.Write(buffer, 0, buffer.Length); } catch (IOException ex) { throw new FtpException(String.Format("Connection is broken. Failed to send command. {0}", ex.Message)); } // most commands will have happy codes but the quote() command does not if (request.HasHappyCodes) { WaitForHappyCodes(request.GetHappyCodes()); } else { // when there are no happy codes given the we have to give the server some time to response // since we really don't know what response is the correct one if (request.Command != FtpCmd.Quit) Thread.Sleep(2000); DontWaitForHappyCodes(); } }