codice:
Imports System.IO
Imports System.Collections.Generic
Public Class FormToMultipartPostData
Private Structure ValuePair
' KeyValuePair<string, string> sounds too verbose for me
Public name As String
Public value As String
Public Sub New(ByVal name As String, ByVal value As String)
Me.name = name
Me.value = value
End Sub
End Structure
Public Structure RequestParameters
Public data As Byte()
Public headers As String
End Structure
Private values As New List(Of ValuePair)()
Private files As New List(Of ValuePair)()
Private overloadedFiles As New Dictionary(Of String, String)()
Private form As HtmlElement
Private webbrowser As WebBrowser
'*
' * In most circumstances, this constructor is better (allows to use Submit() method)
'
Public Sub New(ByVal b As WebBrowser, ByVal f As HtmlElement)
form = f
webbrowser = b
GetValuesFromForm(f)
End Sub
'*
' * Use this constructor if you don't want to use Submit() method
'
Public Sub New(ByVal f As HtmlElement)
GetValuesFromForm(f)
End Sub
'*
' * Submit the form
'
Public Sub Submit()
Dim url As New Uri(webbrowser.Url, form.GetAttribute("action"))
Dim req As RequestParameters = GetEncodedPostData()
webbrowser.Navigate(url, form.GetAttribute("target"), req.data, req.headers)
End Sub
'*
' * Load values from form
'
Private Sub GetValuesFromForm(ByVal form As HtmlElement)
' Get values from the form
For Each child As HtmlElement In form.All
Select Case child.TagName
Case "INPUT"
Select Case child.GetAttribute("type").ToUpper()
Case "FILE"
AddFile(child.Name, child.GetAttribute("value"))
Exit Select
'Case "CHECKBOX", "RADIO"
' 'If child.GetAttribute("checked") = "True" Then
' 'AddValue(child.Name, child.GetAttribute("value"))
' 'End If
' 'Exit Select
'Case "BUTTON", "IMAGE", "RESET"
' Exit Select
Case Else
' Ignore those?
AddValue(child.Name, child.GetAttribute("value"))
Exit Select
End Select
Exit Select
Case "TEXTAREA", "SELECT"
' it's legal in IE to use .value with select (at least in IE versions 3 to 7)
AddValue(child.Name, child.GetAttribute("value"))
Exit Select
' of "switch tagName"
End Select
' of "foreach form child"
Next
End Sub
Private Sub AddValue(ByVal name As String, ByVal value As String)
If name = "" Then
Exit Sub
End If
' e.g. unnamed buttons
values.Add(New ValuePair(name, value))
End Sub
Private Sub AddFile(ByVal name As String, ByVal value As String)
If name = "" Then
Exit Sub
End If
files.Add(New ValuePair(name, value))
End Sub
'*
' * Set file field value [the reason why this class exist]
'
Public Sub SetFile(ByVal fieldName As String, ByVal filePath As String)
Me.overloadedFiles.Add(fieldName, filePath)
End Sub
'*
' * One may need it to know whether there's specific file input
' * For example, to perform some actions (think format conversion) before uploading
'
Public Function HasFileField(ByVal fieldName As String) As Boolean
For Each v As ValuePair In files
If v.name = fieldName Then
Return True
End If
Next
Return False
End Function
'*
' * Encode parameters
' * Based on the code by Steven Cheng, http://bytes.com/forum/thread268661.html
'
Public Function GetEncodedPostData() As RequestParameters
Dim boundary As String = "----------------------------" & DateTime.Now.Ticks.ToString("x")
Dim memStream As Stream = New System.IO.MemoryStream()
Dim boundarybytes As Byte() = System.Text.Encoding.ASCII.GetBytes(vbCr & vbLf & "--" & boundary & vbCr & vbLf)
Dim formdataTemplate As String = vbCr & vbLf & "--" & boundary & vbCr & vbLf & "Content-Disposition: form-data; name=""{0}"";" & vbCr & vbLf & vbCr & vbLf & "{1}"
For Each v As ValuePair In values
Dim formitem As String = String.Format(formdataTemplate, v.name, v.value)
Dim formitembytes As Byte() = System.Text.Encoding.UTF8.GetBytes(formitem)
memStream.Write(formitembytes, 0, formitembytes.Length)
Next
memStream.Write(boundarybytes, 0, boundarybytes.Length)
Dim headerTemplate As String = "Content-Disposition: form-data; name=""{0}""; filename=""{1}""" & vbCr & vbLf & "Content-Type: application/octet-stream" & vbCr & vbLf & vbCr & vbLf
For Each v As ValuePair In files
Dim filePath As String
If overloadedFiles.ContainsKey(v.name) Then
filePath = overloadedFiles(v.name)
Else
If v.value.Length = 0 Then
Continue For
End If
' no file
filePath = v.value
End If
Try
' file can be absent or not readable
Dim fileStream As New FileStream(filePath, FileMode.Open, FileAccess.Read)
Dim header As String = String.Format(headerTemplate, v.name, filePath)
Dim headerbytes As Byte() = System.Text.Encoding.UTF8.GetBytes(header)
memStream.Write(headerbytes, 0, headerbytes.Length)
Dim buffer As Byte() = New Byte(1023) {}
Dim bytesRead As Integer = 0
While (InlineAssignHelper(bytesRead, fileStream.Read(buffer, 0, buffer.Length))) <> 0
memStream.Write(buffer, 0, bytesRead)
End While
memStream.Write(boundarybytes, 0, boundarybytes.Length)
fileStream.Close()
Catch x As Exception
' no file?..
MessageBox.Show(x.Message, "Cannot upload the file", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End Try
Next
Dim result As New RequestParameters()
memStream.Position = 0
result.data = New Byte(memStream.Length - 1) {}
memStream.Read(result.data, 0, result.data.Length)
memStream.Close()
result.headers = ("Content-Type: multipart/form-data; boundary=" & boundary & vbCr & vbLf & "Content-Length: ") + result.data.Length & vbCr & vbLf & vbCr & vbLf
Return result
End Function
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
End Class