Here is a Class I found on the Net.
Imports System
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports System.IO
Imports System.Security.Cryptography
Imports Microsoft.VisualBasic
'/// <summary>Implements a class that can download file over an FTP connection.</summary>
'/// <remarks>In the current implementation, the DownloadLength property will always be negative.</remarks>
Public Class FtpDownloader
Inherits FileDownloader
'/// <summary>Constructs a new FtpDownloader object and initializes the port to 21.</summary>
Public Sub New()
Port = 21
End Sub
'/// <summary>Starts downloading a file.</summary>
'/// <exceptions cref="DownloadException">Thrown when an error occured while downloading the file.</exceptions>
Public Overrides Sub FetchFile()
If IsConnected Then Throw New DownloadException("This object is already downloading something.")
Dim Buffer(1023) as Byte
Dim Ret as Integer
'Initialize the class
m_DownloadLength = -1
'Initialize the things we're going to use
ClearBuffer()
OpenFile()
'Connect to the server
Connect()
Logon()
'Download the file
SendCommand("TYPE I")
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
Throw New DownloadException("Server rejected binary transfer.")
End If
'Create a new data socket
DataSocket = New Socket(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp)
If Passive Then
'Get the server's IP address and port
SendCommand("PASV")
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
Throw New DownloadException("Server rejected passive request.")
End If
'Try to connect to that
Try
DataSocket.Connect(ParsePassiveReply(LastResponse))
Catch
Disconnect()
Throw New DownloadException("Cannot connect to specified endpoint.")
End Try
Else
'Let's listen on a local port
DataSocket.Bind(New IPEndPoint(Dns.Resolve(Dns.GetHostName()).AddressList(0), 0))
DataSocket.Listen(10)
Dim LocalEP as IPEndPoint = CType(DataSocket.LocalEndPoint, IPEndPoint)
'Let the server know which port we're listening on
SendCommand("PORT " + CType(LocalEP, IPEndPoint).Address.ToString().Replace(".", ",") + "," + Math.Floor(LocalEP.Port / 256).ToString + "," + (LocalEP.Port Mod 256).ToString)
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
DataSocket.Close()
Throw New DownloadException("Server rejected PORT command.")
End If
End If
'Resume
If UseResume AndAlso ResumeFrom > 0 Then
SendCommand("REST " + ResumeFrom.ToString)
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
DataSocket.Close()
Throw New DownloadException("Server doesn't support resume.")
End If
End If
'Ask the server to send us the file
SendCommand("RETR " + RequestedFile)
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
DataSocket.Close()
Throw New DownloadException("Download request failed.")
End If
'Download all the bytes
DownloadData()
CloseFile()
'Disconnect from the server
SendCommand("QUIT")
Disconnect()
End Sub
'/// <summary>Logs the user on to the server.</summary>
'/// <exceptions cref="DownloadException">Thrown when there was an error whil logging on.</exceptions>
Private Sub Logon()
WaitForResponse()
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
Throw New DownloadException("Server rejected connection.")
End If
SendCommand("USER " & Username)
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
Throw New DownloadException("Server rejected username.")
End If
SendCommand("PASS " & Password)
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
Throw New DownloadException("Server rejected username/password combination.")
End If
End Sub
'/// <summary>Parses the reply that's received from the remote host after a PASV command has been sent.</summary>
'/// <param name="Reply">The reply from the server.</param>
'/// <exceptions cref="DownloadException">Thrown when the server's reply is invalid.</exceptions>
'/// <returns>Returns the IPEndPoint that's parsed from the input data</returns>
Private Function ParsePassiveReply(Reply as String) as IPEndPoint
Dim BeginPos as Integer = Reply.IndexOf("(")
Dim EndPos as Integer = Reply.IndexOf(")", BeginPos + 1)
Try
If BeginPos > 0 And EndPos > 0 Then
Dim Output() as String = Reply.Substring(BeginPos + 1, EndPos - BeginPos - 1).Split(","c)
If Output.Length() = 6 Then
ParsePassiveReply = New IPEndPoint(IPAddress.Parse(Output(0) + "." + Output(1) + "." + Output(2) + "." + Output(3)), Integer.Parse(Output(4)) * 256 + Integer.Parse(Output(5)))
End If
End If
Catch
Throw New DownloadException("Invalid PASV reply from server.")
End Try
End Function
'/// <summary>Downloads the data from the remote host.</summary>
'/// <exceptions cref="DownloadException">Thrown when there was an error while downloading the data.</exceptions>
Private Sub DownloadData()
Dim Ret as Integer = 0
Dim Buffer(1024) as Byte
Dim theSocket as Socket
Try
If Passive Then
theSocket = DataSocket
Else
theSocket = DataSocket.Accept()
End If
'Receive the reply
Ret = theSocket.Receive(Buffer)
While Ret <> 0 AndAlso IsConnected
HandleBytes(Buffer, Ret)
If ReceivedBytes = MaxDownload Then Exit While
If theSocket.Connected Then
Ret = theSocket.Receive(Buffer)
Else
Ret = 0
End If
End While
Catch
Throw New DownloadException("An error occured while downloading the data from the remote host.")
End Try
theSocket.Close()
If Not Passive Then DataSocket.Close()
WaitForResponse()
If LastResponseType = 4 OrElse LastResponseType = 5 Then
Disconnect()
Throw New DownloadException("Error whle downloading the file.")
End If
End Sub
'/// <summary>Waits until the server sends a reply.</summary>
'/// <exceptions cref="DownloadException">Thrown when there was an error while waiting for a reply.</exceptions>
Private Sub WaitForResponse()
Dim TempBuffer(1023) as Byte
Dim RetBytes as Integer
Dim RetString as String
Try
Do
RetBytes = DownloadSocket.Receive(tempBuffer)
RetString &= Encoding.ASCII.GetString(TempBuffer, 0, RetBytes)
Loop Until IsValidResponse(RetString)
If RetString.Length >= 1 Then
m_LastResponse = retString
m_LastResponseType = Integer.Parse(retString.Substring(0, 1))
Else
m_LastResponse = ""
m_LastResponseType = 5 'Unrecoverable error
End If
Catch
If IsConnected Then Throw New DownloadException("Error while reading from command stream.")
End Try
End Sub
'/// <summary>Determines whether a string returned from an FTP server is a valid reply string.</summary>
'/// <returns>True if it is a valid reply string, False otherwise.</returns>
Private Function IsValidResponse(Input as string)as Boolean
Dim Lines() as String = Input.Split(ControlChars.Lf)
If Lines.Length > 1 Then
Try
If Lines(Lines.Length - 2).Replace(ControlChars.Cr, "").Substring(3, 1).Equals(" ") Then Return True
Catch
Return False
End Try
End If
Return False
End Function
'/// <summary>Sends a command to the remote host.</summary>
'/// <exceptions cref="DownloadException">Thrown when there was an error sending the command.</exceptions>
Private Sub SendCommand(Command as String)
Try
DownloadSocket.Send(Encoding.ASCII.GetBytes(Command & ControlChars.CrLf))
WaitForResponse()
Catch
If IsConnected Then Throw New DownloadException("Error while writing to command stream.")
End Try
End Sub
'/// <summary>Disconnects the object from the remote host.</summary>
Public Shadows Sub Disconnect()
If Not DataSocket Is Nothing Then DataSocket.Close()
MyBase.Disconnect()
End Sub
'/// <summary>Downloads the secified file.</summary>
'/// <param name="File">The file to download.<param>
'/// <exceptions cref="ArgumentException">Thrown when the specified parameter is invalid.</exceptions>
'/// <remarks>
'/// The following type of input is accepted:
'/// ftp://123.1.2.3/thedir/thefile.ext
'/// ftp://123.1.2.3:123/thedir/thefile.ext
'/// ftp://username@123.1.2.3/thedir/thefile.ext
'/// ftp://username@123.1.2.3:123/thedir/thefile.ext
'/// ftp://usernameassword@123.1.2.3/thedir/thefile.ext
'/// ftp://usernameassword@123.1.2.3:123/thedir/thefile.ext
'/// All the above combinations without the 'ftp://'
'/// </remarks>
Public Overrides Sub FetchFileString(File as String)
ParseURL(File)
FetchFile()
End Sub
'/// <summary>Parses a FTP string.</summary>
'/// <param name="URL">The URL to parse.<param>
'/// <exceptions cref="ArgumentException">Thrown when the specified parameter is invalid.</exceptions>
Private Sub ParseURL(URL as String)
URL = URL.Trim
Dim Protocol as Integer = URL.IndexOf("://")
Dim StartHost as Integer, EndHost as Integer
If Protocol > 0 Then
If Not URL.Substring(0, 6).ToLower.Equals("ftp://") Then
Throw New ArgumentException()
End If
StartHost = 6
Else
StartHost = 0
End If
EndHost = URL.IndexOf("/", StartHost)
Dim UrlHost as String = URL.Substring(StartHost, EndHost - StartHost)
Dim Parts() as String = UrlHost.Split("@"c)
Dim UserPass() as String
Dim ServerPort() as String
If Parts.Length = 1 Then
UserPass = New String() {Username, Password}
ServerPort = Parts(0).Split(":"c)
ElseIf Parts.Length = 2 Then
UserPass = Parts(0).Split(":"c)
ServerPort = Parts(1).Split(":"c)
Else
Throw New ArgumentException()
End If
Protocol = Integer.Parse(ServerPort(1))
If Protocol <= 0 Then Throw New ArgumentException()
Port = Protocol
Host = ServerPort(0)
Username = UserPass(0)
Password = UserPass(1)
RequestedFile = URL.Substring(EndHost)
End Sub
'/// <summary>Holds the last response type sent by the server.</summary>
'/// <value>The last response type sent by the server.</value>
Private ReadOnly Property LastResponseType() as Integer
Get
Return m_LastResponseType
End Get
End Property
'/// <summary>Holds the last response sent by the server.</summary>
'/// <value>The last response sent by the server.</value>
Private ReadOnly Property LastResponse() as String
Get
Return m_LastResponse
End Get
End Property
'/// <summary>Gets of sets the data socket.</summary>
'/// <value>The data socket.</value>
Private Property DataSocket() as Socket
Get
Return m_DataSocket
End Get
Set(Value as Socket)
m_DataSocket = Value
End Set
End Property
'/// <summary>Gets or sets a value that specifies whether or not to use passive transfers.</summary>
'/// <value>A value that specifies whether or not to use passive transfers.</value>
Public Property Passive() as Boolean
Get
Return m_Passive
End Get
Set(Value as Boolean)
m_Passive = Value
End Set
End Property
'Private variables
Private m_LastResponseType as Integer = 0
Private m_LastResponse as String = ""
Private m_Passive as Boolean = True
Private m_DataSocket as Socket
End Class
From Here