PDA

View Full Version : Download a file Via Http


robinhood1995
02-08-2004, 01:49 AM
Here's the Class.

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 HTTP connection.</summary>
Public Class HttpDownloader
Inherits FileDownloader
'/// <summary>Constructs a new HttpDownloader object.</summary>
Public Sub New()
Port = 80
End Sub
'/// <summary>Starts downloading from a URL.</summary>
'/// <exceptions cref="DownloadException">Thrown when there was an error 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
HeaderExtracted = False
HeaderPart = ""
m_DownloadLength = -1
Moving = False
'Initialize the things we're going to use
ClearBuffer()
OpenFile()
'Connect to the server
Connect()
'Send the HTTP query
DownloadSocket.Send(Encoding.ASCII.GetBytes(GetQue ryString))
'Receive the reply
Ret = DownloadSocket.Receive(Buffer)
While Ret <> 0
If ExtractHeader(Buffer, Ret) Then
HandleBytes(Buffer, Ret)
End If
If ReceivedBytes = MaxDownload Then Exit While
Ret = DownloadSocket.Receive(Buffer)
End While
CloseFile()
'Disconnect from the server
Disconnect()
End Sub
'/// <summary>Starts downloading from the specified URL.</summary>
'/// <remarks>
'/// The following type of input will be accepted:
'/// http://www.server.com/dir/file.ext
'/// http://www.server.com:1234/dir/file.ext
'/// www.server.com/dir/file.ext
'/// </remarks>
'/// <param name="File">The URL to download.</param>
'/// <exceptions cref="ArgumentException">Thrown when the specified URL is invalid.</exceptions>
'/// <exceptions cref="DownloadException">Thrown when there was an error while downloading the file.</exceptions>
Public Overrides Sub FetchFileString(File as String)
ParseURL(File)
FetchFile()
End Sub
'/// <summary>Parses a HTTP URL.</summary>
'/// <param name="URL">The URL to parse.</param>
'/// <exceptions cref="ArgumentException">Thrown when the specified URL 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, 7).ToLower.Equals("http://") Then
Throw New ArgumentException()
End If
StartHost = 7
Else
StartHost = 0
End If
EndHost = URL.IndexOf("/", StartHost)
Dim UrlHost as String = URL.Substring(StartHost, EndHost - StartHost)
Protocol = UrlHost.IndexOf(":")
If Protocol > 0 Then
Host = UrlHost.SubString(0, Protocol)
Port = Integer.Parse(UrlHost.SubString(Protocol + 1))
Else
Host = UrlHost
Port = 80
End If
RequestedFile = URL
End Sub
'/// <summary>Extracts a HTTP header from a HTTP stream.</summary>
'/// <param name="Buffer">The HTTP stream to process.</param>
'/// <param name="Bytes">The number of bytes in the stream.</param>
'/// <returns>Returns True if the header was successfully extracted, False otherwise.</returns>
Private Function ExtractHeader(ByRef Buffer() as Byte, ByRef Bytes as Integer) as Boolean
If HeaderExtracted Then Return True
Dim Header as String = HeaderPart & Encoding.ASCII.GetString(Buffer, 0, Bytes)
Dim Ret as Integer = Header.IndexOf(ControlChars.CrLf & ControlChars.CrLf) - HeaderPart.Length
HeaderPart = Header
If Ret <> -1 Then
Bytes = Bytes - Ret - 4
Array.Copy(Buffer, Ret + 4, Buffer, 0, Bytes)
HeaderExtracted = True
Return ProcessHeader(Header.Substring(0, Ret))
End If
Return False
End Function
'/// <summary>Returns the query string that will be sent to the remote host.</summary>
'/// <returns>Returns the query string that will be sent to the remote host.</returns>
Private Function GetQueryString() as String
Dim QueryString as String = "GET " & RequestedFile & " HTTP/1.0" & ControlChars.CrLf
QueryString &= "Accept: */*" & ControlChars.CrLf & "User-Agent: AllAPI.net Download Class" & ControlChars.CrLf & "Host: " & Host & ControlChars.CrLf
'Check for resume
If UseResume And ResumeFrom > 0 Then
QueryString &= "Range: bytes=" & ResumeFrom.ToString & "-" & ControlChars.CrLf
End If
'Check for HTTP Authentication
If Username <> "" Then
QueryString &= "Authorization: Basic " & Convert.ToBase64String(Encoding.ASCII.GetBytes(Use rname & ":" & Password)) & ControlChars.CrLf
End If
'Check whether or not to allow cache
If Not AllowCache Then
QueryString &= "Pragma: no-cache" & ControlChars.CrLf & "Cache-Control: no-cache" & vbCrLf
End If
'Send the query
QueryString &= ControlChars.CrLf
Return QueryString
End Function
'/// <summary>Processes a HTTP header.</summary>
'/// <param name="Header">The HTTP header to process.</param>
'/// <returns>Returns True if the header has been successfully processed, False otherwise.</returns>
'/// <exceptions cref="DownloadException">Thrown when the header is invalid.</exceptions>
Private Function ProcessHeader(Header as String) as Boolean
Dim Lines() as String = Header.Split(New Char() {ControlChars.Cr, ControlChars.Lf})
Dim Cnt as Integer, ColumnPos as Integer
Dim NewLocation as String
For Cnt = 1 to Lines.Length - 1
Lines(Cnt) = Lines(Cnt).Trim
ColumnPos = Lines(Cnt).IndexOf(":")
If ColumnPos > 0 Then
Select Case Lines(Cnt).Substring(0, ColumnPos).ToLower
Case "content-length" 'Content-Length: 12345
m_DownloadLength = Integer.Parse(Lines(Cnt).Substring(ColumnPos + 1))
Case "location" 'Location: www.newplace.com
NewLocation = Lines(Cnt).Substring(ColumnPos + 1).Trim
End Select
End If
Next Cnt
' Process the first header line
Dim ServerReply as Integer
Try
Lines = Lines(0).Split(" "c)
ServerReply = Integer.Parse(Lines(1))
Catch
Throw New DownloadException("Unknown server reply.")
End Try
Select Case ServerReply
Case 206 ' "Partial data" reply
If Not UseResume Then
Throw New DownloadException("Server did not return the entire file.")
End If
Case 200 ' "OK" reply
If UseResume And ResumeFrom > 0 Then
Throw New DownloadException("Server cannot resume.")
End If
Case 301, 302 ' "Moved to" reply
If Not AutoRedirect Then
Throw New DownloadException("File moved.")
End If
If NewLocation = "" Then
Throw New DownloadException("Invalid new location.")
End If
Disconnect(False)
HeaderExtracted = False
HeaderPart = ""
ParseURL(NewLocation)
Connect(False)
DownloadSocket.Send(Encoding.ASCII.GetBytes(GetQue ryString))
Return False
Case Else
Throw New DownloadException("Unknown server reply.")
End Select
Return True
End Function
'/// <summary>Gets or sets a value that specifies whether to allow cached data on proxies or not.</summary>
'/// <value>A value that specifies whether to allow cached data on proxies or not.</value>
Public Property AllowCache() as Boolean
Get
Return m_AllowCache
End Get
Set(Value as Boolean)
m_AllowCache = Value
End Set
End Property
'/// <summary>Gets or sets a value that specifies whether to auto-redirect when a URI has moved.</summary>
'/// <remarks>If this value is set to False and the URI has moved, a DownloadException will be thrown when calling the FetchFile method.</remarks>
'/// <value>A value that specifies whether to auto-redirect when a URI has moved.</value>
Public Property AutoRedirect() as Boolean
Get
Return m_AutoRedirect
End Get
Set(Value as Boolean)
m_AutoRedirect = Value
End Set
End Property
'/// <summary>Gets or sets a value that specifies whether the header has already been extracted from the data or not.</summary>
'/// <value>A value that specifies whether the header has already been extracted from the data or not.</value>
Private Property HeaderExtracted() as Boolean
Get
Return m_HeaderExtracted
End Get
Set(Value as Boolean)
m_HeaderExtracted = Value
End Set
End Property
'/// <summary>Gets or sets a string that contains (parts of) the HTTP header.</summary>
'/// <value>A string that contains (parts of) the HTTP header.</value>
Private Property HeaderPart() as String
Get
Return m_HeaderPart
End Get
Set(Value as String)
m_HeaderPart = Value
End Set
End Property
'/// <summary>Gets or sets a value that specifies whether the object is currently moving to another URL.</summary>
'/// <value>A value that specifies whether the object is currently moving to another URL.</value>
Private Property Moving() as Boolean
Get
Return m_Moving
End Get
Set(Value as Boolean)
m_Moving = Value
End Set
End Property
'Private variables
Private m_AllowCache as Boolean = False
Private m_AutoRedirect as Boolean = True
Private m_HeaderExtracted as Boolean
Private m_HeaderPart as String = ""
Private m_Moving as Boolean = False
End Class