M
Mark Waser
Hi all,
I'm trying to post multipart/form-data to a web page but seem to have
run into a wall. I'm familiar with RFC 1867 and have done this before (with
AOLServer and Tcl) but just can't seem to get it to work in Visual Basic. I
tried coding it once myself from scratch and then modified a class that I
found on a newsgroup (referenced below). Both seem to be doing the same
thing and neither works (or rather, they seem to work but the server doesn't
see the variables that I am attempting to pass to it). Can anyone either a)
direct me to some code that they KNOW is functional or b) give me any
pointers to where I might be going wrong?
The second set of code that I'm using is copied below and is translated
from c# from the following newsgroup:
http://groups.google.com/groups?q=norvanco.com&hl=en&lr=&ie=UTF-8&oe=UTF-8&s
elm=OaDlKeDTDHA.2144%40TK2MSFTNGP11.phx.gbl&rnum=6
If anyone could help, I would greatly appreciate it.
Mark
Imports System
Imports System.Net
Imports System.Text
Imports System.IO
Imports System.Collections
Module Module1
'/// <summary>
'/// Allow the transfer of data files using the W3C's specification
'/// for HTTP multipart form data. Microsoft's version has a bug
'/// where it does not format the ending boundary correctly.
'/// Written by: (e-mail address removed)
'/// </summary>
Public Class MultipartForm
Public Cookies As CookieContainer
'/// <summary>
'/// Holds any form fields and values that you
'/// wish to transfer with your data.
'/// </summary>
Private coFormFields As Hashtable
'/// <summary>
'/// Used mainly to avoid passing parameters to other routines.
'/// Could have been local to sendFile().
'/// </summary>
Protected coRequest As HttpWebRequest
'/// <summary>
'/// Used if we are testing and want to output the raw
'/// request, minus http headers, out to a file.
'/// </summary>
Protected coFileStream As System.IO.Stream
'/// <summary>
'/// Defined to build the form field data that is being
'/// passed along with the request.
'/// </summary>
Dim CONTENT_DISP As String = "Content-Disposition: form-data; name="
'/// <summary>
'/// Allows you to specify the specific version of HTTP to use for uploads.
'/// The dot NET stuff currently does not allow you to remove the
continue-100 header
'/// from 1.1 and 1.0 currently has a bug in it where it adds the
continue-100. MS
'/// has sent a patch to remove the continue-100 in HTTP 1.0.
'/// </summary>
' public Version TransferHttpVersion
' {get{return coHttpVersion;}set{coHttpVersion=value;}}
' Version coHttpVersion;
Public TransferHttpVersion As Version
'/// <summary>
'/// Used to change the content type of the file being sent.
'/// Currently defaults to: text/xml. Other options are
'/// text/plain or binary
'/// </summary>
'public string FileContentType
'{get{return coFileContentType;}set{coFileContentType=value;}}
'string coFileContentType;
Public FileContentType As String
'/// <summary>
'/// Initialize our class for use to send data files.
'/// </summary>
'/// <param name="url">The web address of the recipient of the data
transfer.</param>
Public Sub New(ByVal _url As String)
URL = _url
coFormFields = New Hashtable()
ResponseText = New StringBuilder()
BufferSize = 1024 * 10
BeginBoundary = "BoundarySepR8R"
TransferHttpVersion = HttpVersion.Version11
FileContentType = "text/xml"
End Sub
'//---------- BEGIN PROPERTIES SECTION ----------
Dim _BeginBoundary As String
'/// <summary>
'/// The string that defines the begining boundary of
'/// our multipart transfer as defined in the w3c specs.
'/// This method also sets the Content and Ending
'/// boundaries as defined by the w3c specs.
'/// </summary>
Public Property BeginBoundary() As String
Get
Return _BeginBoundary
End Get
Set(ByVal Value As String)
_BeginBoundary = Value
ContentBoundary = "--" + BeginBoundary
EndingBoundary = ContentBoundary + "--"
End Set
End Property
'/// <summary>
'/// The string that defines the content boundary of
'/// our multipart transfer as defined in the w3c specs.
'/// </summary>
Protected ContentBoundary As String
'/// <summary>
'/// The string that defines the ending boundary of
'/// our multipart transfer as defined in the w3c specs.
'/// </summary>
Protected EndingBoundary As String
'/// <summary>
'/// The data returned to us after the transfer is completed.
'/// </summary>
Public ResponseText As StringBuilder
'/// <summary>
'/// The web address of the recipient of the transfer.
'/// </summary>
Public URL As String
'/// <summary>
'/// Allows us to determine the size of the buffer used
'/// to send a piece of the file at a time out the IO
'/// stream. Defaults to 1024 * 10.
'/// </summary>
Public BufferSize As Integer
'//---------- END PROPERTIES SECTION ----------
'/// <summary>
'/// Used to signal we want the output to go to a
'/// text file verses being transfered to a URL.
'/// </summary>
'/// <param name="path"></param>
Public Sub setFilename(ByVal path As String)
coFileStream = New System.IO.FileStream(path, FileMode.Create,
FileAccess.Write)
End Sub
'/// <summary>
'/// Allows you to add some additional field data to be
'/// sent along with the transfer. This is usually used
'/// for things like userid and password to validate the
'/// transfer.
'/// </summary>
'/// <param name="key">The form field name</param>
'/// <param name="str">The form field value</param>
Public Sub setField(ByVal key As String, ByVal str As String)
coFormFields.Add(key, str)
End Sub
'/// <summary>
'/// Determines if we have a file stream set, and returns either
'/// the HttpWebRequest stream of the file.
'/// </summary>
'/// <returns></returns>
Public Function getStream() As System.IO.Stream
Dim io As System.IO.Stream
If (coFileStream Is Nothing) Then
io = coRequest.GetRequestStream()
Else
io = coFileStream
End If
Return io
End Function
'/// <summary>
'/// Here we actually make the request to the web server and
'/// retrieve it's response into a text buffer.
'/// </summary>
Public Sub getResponse()
If (coFileStream Is Nothing) Then
Dim io As System.IO.Stream
Dim oResponse As WebResponse
Try
oResponse = coRequest.GetResponse()
Catch web As WebException
oResponse = web.Response
End Try
If (Not (oResponse Is Nothing)) Then
io = oResponse.GetResponseStream()
Dim sr As StreamReader = New StreamReader(io)
Dim str As String
ResponseText.Length = 0
str = sr.ReadLine()
While (Not (str Is Nothing))
ResponseText.Append(str)
str = sr.ReadLine()
End While
oResponse.Close()
Else
Throw New Exception("MultipartForm: Error retrieving server response")
End If
End If
End Sub
'/// <summary>
'/// Transmits a file to the web server stated in the
'/// URL property. You may call this several times and it
'/// will use the values previously set for fields and URL.
'/// </summary>
'/// <param name="aFilename">The full path of file being transfered.</param>
Public Sub sendFile(ByVal aFilename As String)
'// The live of this object is only good during
'// this function. Used mainly to avoid passing
'// around parameters to other functions.
coRequest = WebRequest.Create(URL)
coRequest.CookieContainer = Cookies
'// Set use HTTP 1.0 or 1.1.
coRequest.ProtocolVersion = TransferHttpVersion
coRequest.Method = "POST"
coRequest.ContentType = "multipart/form-data, boundary=" + BeginBoundary
coRequest.Headers.Add("Cache-Control", "no-cache")
coRequest.KeepAlive = True
Dim strFields As String = getFormfields()
Dim strFileHdr As String = getFileheader(aFilename)
Dim strFileTlr As String = getFiletrailer()
Dim info As FileInfo = New FileInfo(aFilename)
coRequest.ContentLength = strFields.Length + strFileHdr.Length +
strFileTlr.Length + info.Length
Dim io As System.IO.Stream
io = getStream()
writeString(io, strFields)
writeString(io, strFileHdr)
writeFile(io, aFilename)
'writeString(io, "fast as fast can be, you'll never catch me")
writeString(io, strFileTlr)
io.Close()
MsgBox(coRequest.Headers.ToString)
getResponse()
'// End the life time of this request object.
coRequest = Nothing
End Sub
'/// <summary>
'/// Mainly used to turn the string into a byte buffer and then
'/// write it to our IO stream.
'/// </summary>
'/// <param name="io">The io stream for output.</param>
'/// <param name="str">The data to write.</param>
Public Sub writeString(ByVal io As System.IO.Stream, ByVal str As String)
Dim PostData As Byte() = System.Text.Encoding.ASCII.GetBytes(str)
io.Write(PostData, 0, PostData.Length)
End Sub
'/// <summary>
'/// Builds the proper format of the multipart data that
'/// contains the form fields and their respective values.
'/// </summary>
'/// <returns>The data to send in the multipart upload.</returns>
Public Function getFormfields() As String
Dim str As String = ""
Dim myEnumerator As IDictionaryEnumerator = coFormFields.GetEnumerator()
While (myEnumerator.MoveNext())
str += ContentBoundary + vbCrLf + CONTENT_DISP + """" + myEnumerator.Key +
"""" + vbCrLf + vbCrLf + myEnumerator.Value + vbCrLf
End While
Return str
End Function
'/// <summary>
'/// Returns the proper content information for the
'/// file we are sending.
'/// </summary>
'/// <remarks>
'/// Hits Patel reported a bug when used with ActiveFile.
'/// Added semicolon after sendfile to resolve that issue.
'/// Tested for compatibility with IIS 5.0 and Java.
'/// </remarks>
'/// <param name="aFilename"></param>
'/// <returns></returns>
Public Function getFileheader(ByVal aFilename As String) As String
Return ContentBoundary + vbCrLf + CONTENT_DISP + """userfile""; filename="""
+ Path.GetFileName(aFilename) + """" + vbCrLf + "Content-type: " +
FileContentType + vbCrLf + vbCrLf
End Function
'/// <summary>
'/// Creates the proper ending boundary for the multipart upload.
'/// </summary>
'/// <returns>The ending boundary.</returns>
Public Function getFiletrailer() As String
Return vbCrLf + EndingBoundary
End Function
'/// <summary>
'/// Reads in the file a chunck at a time then sends it to the
'/// output stream.
'/// </summary>
'/// <param name="io">The io stream to write the file to.</param>
'/// <param name="aFilename">The name of the file to transfer.</param>
Public Sub writeFile(ByVal io As System.IO.Stream, ByVal aFilename As
String)
Dim readIn As FileStream = New FileStream(aFilename, FileMode.Open,
FileAccess.Read)
readIn.Seek(0, SeekOrigin.Begin) ' move to the start of the file
Dim fileData(BufferSize) As Byte
Dim bytes As Integer
bytes = readIn.Read(fileData, 0, BufferSize)
While (bytes > 0)
'// read the file data and send a chunk at a time
io.Write(fileData, 0, bytes)
bytes = readIn.Read(fileData, 0, BufferSize)
End While
readIn.Close()
End Sub
End Class
End Module
--
Charity: Give a man a fish, feed him for a day
Education: Teach a man to fish, feed him his entire life
Humanism: Teach a man to believe in himself, and he will learn more than
just how to fish.
Teach a man to believe in mankind ....
I'm trying to post multipart/form-data to a web page but seem to have
run into a wall. I'm familiar with RFC 1867 and have done this before (with
AOLServer and Tcl) but just can't seem to get it to work in Visual Basic. I
tried coding it once myself from scratch and then modified a class that I
found on a newsgroup (referenced below). Both seem to be doing the same
thing and neither works (or rather, they seem to work but the server doesn't
see the variables that I am attempting to pass to it). Can anyone either a)
direct me to some code that they KNOW is functional or b) give me any
pointers to where I might be going wrong?
The second set of code that I'm using is copied below and is translated
from c# from the following newsgroup:
http://groups.google.com/groups?q=norvanco.com&hl=en&lr=&ie=UTF-8&oe=UTF-8&s
elm=OaDlKeDTDHA.2144%40TK2MSFTNGP11.phx.gbl&rnum=6
If anyone could help, I would greatly appreciate it.
Mark
Imports System
Imports System.Net
Imports System.Text
Imports System.IO
Imports System.Collections
Module Module1
'/// <summary>
'/// Allow the transfer of data files using the W3C's specification
'/// for HTTP multipart form data. Microsoft's version has a bug
'/// where it does not format the ending boundary correctly.
'/// Written by: (e-mail address removed)
'/// </summary>
Public Class MultipartForm
Public Cookies As CookieContainer
'/// <summary>
'/// Holds any form fields and values that you
'/// wish to transfer with your data.
'/// </summary>
Private coFormFields As Hashtable
'/// <summary>
'/// Used mainly to avoid passing parameters to other routines.
'/// Could have been local to sendFile().
'/// </summary>
Protected coRequest As HttpWebRequest
'/// <summary>
'/// Used if we are testing and want to output the raw
'/// request, minus http headers, out to a file.
'/// </summary>
Protected coFileStream As System.IO.Stream
'/// <summary>
'/// Defined to build the form field data that is being
'/// passed along with the request.
'/// </summary>
Dim CONTENT_DISP As String = "Content-Disposition: form-data; name="
'/// <summary>
'/// Allows you to specify the specific version of HTTP to use for uploads.
'/// The dot NET stuff currently does not allow you to remove the
continue-100 header
'/// from 1.1 and 1.0 currently has a bug in it where it adds the
continue-100. MS
'/// has sent a patch to remove the continue-100 in HTTP 1.0.
'/// </summary>
' public Version TransferHttpVersion
' {get{return coHttpVersion;}set{coHttpVersion=value;}}
' Version coHttpVersion;
Public TransferHttpVersion As Version
'/// <summary>
'/// Used to change the content type of the file being sent.
'/// Currently defaults to: text/xml. Other options are
'/// text/plain or binary
'/// </summary>
'public string FileContentType
'{get{return coFileContentType;}set{coFileContentType=value;}}
'string coFileContentType;
Public FileContentType As String
'/// <summary>
'/// Initialize our class for use to send data files.
'/// </summary>
'/// <param name="url">The web address of the recipient of the data
transfer.</param>
Public Sub New(ByVal _url As String)
URL = _url
coFormFields = New Hashtable()
ResponseText = New StringBuilder()
BufferSize = 1024 * 10
BeginBoundary = "BoundarySepR8R"
TransferHttpVersion = HttpVersion.Version11
FileContentType = "text/xml"
End Sub
'//---------- BEGIN PROPERTIES SECTION ----------
Dim _BeginBoundary As String
'/// <summary>
'/// The string that defines the begining boundary of
'/// our multipart transfer as defined in the w3c specs.
'/// This method also sets the Content and Ending
'/// boundaries as defined by the w3c specs.
'/// </summary>
Public Property BeginBoundary() As String
Get
Return _BeginBoundary
End Get
Set(ByVal Value As String)
_BeginBoundary = Value
ContentBoundary = "--" + BeginBoundary
EndingBoundary = ContentBoundary + "--"
End Set
End Property
'/// <summary>
'/// The string that defines the content boundary of
'/// our multipart transfer as defined in the w3c specs.
'/// </summary>
Protected ContentBoundary As String
'/// <summary>
'/// The string that defines the ending boundary of
'/// our multipart transfer as defined in the w3c specs.
'/// </summary>
Protected EndingBoundary As String
'/// <summary>
'/// The data returned to us after the transfer is completed.
'/// </summary>
Public ResponseText As StringBuilder
'/// <summary>
'/// The web address of the recipient of the transfer.
'/// </summary>
Public URL As String
'/// <summary>
'/// Allows us to determine the size of the buffer used
'/// to send a piece of the file at a time out the IO
'/// stream. Defaults to 1024 * 10.
'/// </summary>
Public BufferSize As Integer
'//---------- END PROPERTIES SECTION ----------
'/// <summary>
'/// Used to signal we want the output to go to a
'/// text file verses being transfered to a URL.
'/// </summary>
'/// <param name="path"></param>
Public Sub setFilename(ByVal path As String)
coFileStream = New System.IO.FileStream(path, FileMode.Create,
FileAccess.Write)
End Sub
'/// <summary>
'/// Allows you to add some additional field data to be
'/// sent along with the transfer. This is usually used
'/// for things like userid and password to validate the
'/// transfer.
'/// </summary>
'/// <param name="key">The form field name</param>
'/// <param name="str">The form field value</param>
Public Sub setField(ByVal key As String, ByVal str As String)
coFormFields.Add(key, str)
End Sub
'/// <summary>
'/// Determines if we have a file stream set, and returns either
'/// the HttpWebRequest stream of the file.
'/// </summary>
'/// <returns></returns>
Public Function getStream() As System.IO.Stream
Dim io As System.IO.Stream
If (coFileStream Is Nothing) Then
io = coRequest.GetRequestStream()
Else
io = coFileStream
End If
Return io
End Function
'/// <summary>
'/// Here we actually make the request to the web server and
'/// retrieve it's response into a text buffer.
'/// </summary>
Public Sub getResponse()
If (coFileStream Is Nothing) Then
Dim io As System.IO.Stream
Dim oResponse As WebResponse
Try
oResponse = coRequest.GetResponse()
Catch web As WebException
oResponse = web.Response
End Try
If (Not (oResponse Is Nothing)) Then
io = oResponse.GetResponseStream()
Dim sr As StreamReader = New StreamReader(io)
Dim str As String
ResponseText.Length = 0
str = sr.ReadLine()
While (Not (str Is Nothing))
ResponseText.Append(str)
str = sr.ReadLine()
End While
oResponse.Close()
Else
Throw New Exception("MultipartForm: Error retrieving server response")
End If
End If
End Sub
'/// <summary>
'/// Transmits a file to the web server stated in the
'/// URL property. You may call this several times and it
'/// will use the values previously set for fields and URL.
'/// </summary>
'/// <param name="aFilename">The full path of file being transfered.</param>
Public Sub sendFile(ByVal aFilename As String)
'// The live of this object is only good during
'// this function. Used mainly to avoid passing
'// around parameters to other functions.
coRequest = WebRequest.Create(URL)
coRequest.CookieContainer = Cookies
'// Set use HTTP 1.0 or 1.1.
coRequest.ProtocolVersion = TransferHttpVersion
coRequest.Method = "POST"
coRequest.ContentType = "multipart/form-data, boundary=" + BeginBoundary
coRequest.Headers.Add("Cache-Control", "no-cache")
coRequest.KeepAlive = True
Dim strFields As String = getFormfields()
Dim strFileHdr As String = getFileheader(aFilename)
Dim strFileTlr As String = getFiletrailer()
Dim info As FileInfo = New FileInfo(aFilename)
coRequest.ContentLength = strFields.Length + strFileHdr.Length +
strFileTlr.Length + info.Length
Dim io As System.IO.Stream
io = getStream()
writeString(io, strFields)
writeString(io, strFileHdr)
writeFile(io, aFilename)
'writeString(io, "fast as fast can be, you'll never catch me")
writeString(io, strFileTlr)
io.Close()
MsgBox(coRequest.Headers.ToString)
getResponse()
'// End the life time of this request object.
coRequest = Nothing
End Sub
'/// <summary>
'/// Mainly used to turn the string into a byte buffer and then
'/// write it to our IO stream.
'/// </summary>
'/// <param name="io">The io stream for output.</param>
'/// <param name="str">The data to write.</param>
Public Sub writeString(ByVal io As System.IO.Stream, ByVal str As String)
Dim PostData As Byte() = System.Text.Encoding.ASCII.GetBytes(str)
io.Write(PostData, 0, PostData.Length)
End Sub
'/// <summary>
'/// Builds the proper format of the multipart data that
'/// contains the form fields and their respective values.
'/// </summary>
'/// <returns>The data to send in the multipart upload.</returns>
Public Function getFormfields() As String
Dim str As String = ""
Dim myEnumerator As IDictionaryEnumerator = coFormFields.GetEnumerator()
While (myEnumerator.MoveNext())
str += ContentBoundary + vbCrLf + CONTENT_DISP + """" + myEnumerator.Key +
"""" + vbCrLf + vbCrLf + myEnumerator.Value + vbCrLf
End While
Return str
End Function
'/// <summary>
'/// Returns the proper content information for the
'/// file we are sending.
'/// </summary>
'/// <remarks>
'/// Hits Patel reported a bug when used with ActiveFile.
'/// Added semicolon after sendfile to resolve that issue.
'/// Tested for compatibility with IIS 5.0 and Java.
'/// </remarks>
'/// <param name="aFilename"></param>
'/// <returns></returns>
Public Function getFileheader(ByVal aFilename As String) As String
Return ContentBoundary + vbCrLf + CONTENT_DISP + """userfile""; filename="""
+ Path.GetFileName(aFilename) + """" + vbCrLf + "Content-type: " +
FileContentType + vbCrLf + vbCrLf
End Function
'/// <summary>
'/// Creates the proper ending boundary for the multipart upload.
'/// </summary>
'/// <returns>The ending boundary.</returns>
Public Function getFiletrailer() As String
Return vbCrLf + EndingBoundary
End Function
'/// <summary>
'/// Reads in the file a chunck at a time then sends it to the
'/// output stream.
'/// </summary>
'/// <param name="io">The io stream to write the file to.</param>
'/// <param name="aFilename">The name of the file to transfer.</param>
Public Sub writeFile(ByVal io As System.IO.Stream, ByVal aFilename As
String)
Dim readIn As FileStream = New FileStream(aFilename, FileMode.Open,
FileAccess.Read)
readIn.Seek(0, SeekOrigin.Begin) ' move to the start of the file
Dim fileData(BufferSize) As Byte
Dim bytes As Integer
bytes = readIn.Read(fileData, 0, BufferSize)
While (bytes > 0)
'// read the file data and send a chunk at a time
io.Write(fileData, 0, bytes)
bytes = readIn.Read(fileData, 0, BufferSize)
End While
readIn.Close()
End Sub
End Class
End Module
--
Charity: Give a man a fish, feed him for a day
Education: Teach a man to fish, feed him his entire life
Humanism: Teach a man to believe in himself, and he will learn more than
just how to fish.
Teach a man to believe in mankind ....