【问题标题】:VB.NET - multipart form upload not working. What's wrong?VB.NET - 多部分表单上传不起作用。怎么了?
【发布时间】:2026-01-08 14:30:03
【问题描述】:

我尝试搜索并发现有人编写了一个多部分表单上传器。

虽然它有一个问题:它传输了所有 - 据称 - url 中的 POST 值。网络应用程序不喜欢 - 不知何故。

一切顺利

POST /index.php?page=post&s=add&title=test&tags=testtags HTTP/1.1

并且只将文件添加到帖子内容中。

我尝试用浏览器提交,结果如下:

POST /index.php?page=post&s=add HTTP/1.1
Host: gelbooru.com

Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.0 (KHTML, like Gecko) Chrome/3.0.195.38 Safari/532.0
Referer: http://gelbooru.com/index.php?page=post&s=add
Content-Length: 54851
Cache-Control: max-age=0
Origin: http://gelbooru.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryFqdVQ+1blrPMX2py
Accept: application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding: gzip,deflate,sdch
Cookie: __utmz=52483902.1258558959.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none);user_id=7382; pass_hash=lolpasswordcensored; __utma=52483902.1527465380.1258558959.1261431208.1261504455.7; __utmc=52483902; __utmb=52483902.1.10.1261504455
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

------WebKitFormBoundaryFqdVQ+1blrPMX2py
Content-Disposition: form-data; name="upload"; filename="001.jpg"
Content-Type: image/jpeg

IMGDATA
------WebKitFormBoundaryFqdVQ+1blrPMX2py
Content-Disposition: form-data; name="source"


------WebKitFormBoundaryFqdVQ+1blrPMX2py
Content-Disposition: form-data; name="title"


------WebKitFormBoundaryFqdVQ+1blrPMX2py
Content-Disposition: form-data; name="tags"

tagtests
------WebKitFormBoundaryFqdVQ+1blrPMX2py
Content-Disposition: form-data; name="rating"

e
------WebKitFormBoundaryFqdVQ+1blrPMX2py
Content-Disposition: form-data; name="submit"

Upload
------WebKitFormBoundaryFqdVQ+1blrPMX2py--

我用 IMGDATA 替换了图像数据字节以使其更短。

此上传已被接受。所以我尝试用我将很快展示的 VB.NET 代码来模仿它。 结果出来了:

POST /index.php?page=post&s=add& HTTP/1.1
Cookie: user_id=7382; pass_hash=lolpass_hashcensor;
Origin: http://gelbooru.com
Referer: http://gelbooru.com/index.php?page=post&s=add
Content-Type: multipart/form-data; boundary=----------8cc5152e90b3c60
Host: gelbooru.com
Content-Length: 243397
Expect: 100-continue
Connection: Keep-Alive


HTTP/1.1 100 Continue


----------8cc5152e90b3c60
Content-Disposition: form-data; name="tags"

tagtest
----------8cc5152e90b3c60
Content-Disposition: form-data; name="source"


----------8cc5152e90b3c60
Content-Disposition: form-data; name="rating"

e
----------8cc5152e90b3c60
Content-Disposition: form-data; name="title"


----------8cc5152e90b3c60
Content-Disposition: form-data; name="submit"

Upload
----------8cc5152e90b3c60
Content-Disposition: form-data; name="upload"; filename="1017E.jpg"
Content-Type: application/octet-stream

FILE
----------8cc5152e90b3c60

但这并没有被接受。那么问题出在哪里?我还尝试将两个额外的(--)放在其他地方。最后,它没有改变任何东西。这是我用于传输的 VB.NET 代码的剪切。 Uploader.Start 通过使用全局变量的线程启动上传过程。

Sub StartUploadJob(ByVal filepath As String, ByVal tags As String, ByVal title As String, ByVal source As String, ByVal rate As rating)
    ContinueProgressLoop = True
    processloop = New Threading.Thread(AddressOf ProgressLoopThread)
    Uploader = New Threading.Thread(AddressOf UploadFileEx)
    uploadfile = filepath

    querystring("page") = "post"
    querystring("s") = "add"
    'http://gelbooru.com/index.php?page=dapi&s=post&q=index
    'querystring("page") = "dapi"
    'querystring("s") = "post"
    'querystring("q") = "index"
    'url = "http://gelbooru.com/index.php"
    url = "http://sinni800.ath.cx/UploadTest.aspx"
    'url = "http://gelbooru.com/ok.php"
    fileFormName = "upload"
    contenttype = "application/octet-stream"
    uploadstrings("tags") = tags
    uploadstrings("source") = source
    uploadstrings("rating") = rate.ToString
    uploadstrings("title") = title
    'querystring("user_id") = My.Settings("user_id")
    'querystring("pass_hash") = My.Settings("pass_hash")
    uploadstrings("submit") = "Upload"
    working = True
    cookies = New CookieContainer()
    'cookies.Add(My.Settings.cookies)
    Uploader.Start()
    processloop.Start()

End Sub

Dim uploadstrings As New NameValueCollection
Dim uploadfile As String : Dim url As String : Dim fileFormName As String : Dim contenttype As String : Dim querystring As New NameValueCollection : Dim cookies As CookieContainer
Function UploadFileEx() '(ByVal uploadfile As String, ByVal url As String, ByVal fileFormName As String, ByVal contenttype As String, ByVal querystring As NameValueCollection, ByVal cookies As CookieContainer) As String
    If (fileFormName Is Nothing) OrElse (fileFormName.Length = 0) Then
        fileFormName = "file"
    End If

    If (contenttype Is Nothing) OrElse (contenttype.Length = 0) Then
        contenttype = "application/octet-stream"
    End If


    Dim postdata As String
    postdata = "?"
    If querystring IsNot Nothing Then
        For Each key As String In querystring.Keys
            postdata += (key & "=") + querystring.[Get](key) & "&"
        Next
    End If
    Dim uri As New Uri(url + "?page=post&s=add")


    Dim boundary As String = "----------" & DateTime.Now.Ticks.ToString("x")
    Dim webrequest__1 As HttpWebRequest = DirectCast(WebRequest.Create(uri), HttpWebRequest)
    'webrequest__1.CookieContainer = cookies
    webrequest__1.Headers(HttpRequestHeader.Cookie) = My.Settings.cookie_string
    webrequest__1.Headers.Add("Origin", "http://gelbooru.com")
    webrequest__1.Referer = "http://gelbooru.com/index.php?page=post&s=add"
    webrequest__1.ContentType = "multipart/form-data; boundary=" & boundary
    webrequest__1.Method = "POST"


    ' Build up the post message header
    Dim sb As New StringBuilder()

    For Each key As String In uploadstrings.Keys
        '    content-disposition: form-data; name="field1"
        'content-type: text/plain

        sb.Append(boundary & vbCrLf)
        sb.Append("Content-Disposition: form-data; name=""" + key + """" & vbCrLf)
        ' sb.Append("content-type: text/plain" & vbCrLf)
        sb.Append(vbCrLf)
        sb.Append(uploadstrings(key))
        sb.Append(vbCrLf)

    Next

    Dim close As Byte() = Encoding.UTF8.GetBytes("--")

    sb.Append("--")
    sb.Append(boundary)
    sb.Append(vbCr & vbLf)
    sb.Append("Content-Disposition: form-data; name=""")
    sb.Append(fileFormName)
    sb.Append("""; filename=""")
    sb.Append(Path.GetFileName(uploadfile))
    sb.Append("""")
    sb.Append(vbCr & vbLf)
    sb.Append("Content-Type: ")
    sb.Append(contenttype)
    sb.Append(vbCr & vbLf)
    sb.Append(vbCr & vbLf)

    Dim postHeader As String = sb.ToString()
    Dim postHeaderBytes As Byte() = Encoding.UTF8.GetBytes(postHeader)

    ' Build the trailing boundary string as a byte array
    ' ensuring the boundary appears on a line by itself
    Dim boundaryBytes As Byte() = Encoding.ASCII.GetBytes(vbCr & vbLf & boundary & "--" & vbCr & vbLf)

    Dim fileStream As New FileStream(uploadfile, FileMode.Open, FileAccess.Read)
    Dim length As Long = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length
    webrequest__1.ContentLength = length

    Dim requestStream As Stream = webrequest__1.GetRequestStream()

    Dim fulllength As Integer = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length

    RaiseEvent TextMessage("")

    ' Write out our post header
    RaiseEvent TextMessage("Write out our post header")
    requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length)
    progress = New Progress_Struct(100 / fulllength * postHeaderBytes.Length, postHeaderBytes.Length, fulllength)
    ' Write out the file contents
    RaiseEvent TextMessage("Write out the file contents")
    Dim buffer As Byte() = New [Byte](CUInt(Math.Min(4096, CInt(fileStream.Length))) - 1) {}
    Dim bytesRead As Integer = 0
    While (InlineAssignHelper(bytesRead, fileStream.Read(buffer, 0, buffer.Length))) <> 0
        requestStream.Write(buffer, 0, bytesRead)
        Progress = New Progress_Struct(100 / fulllength * (Progress.bytes_read + buffer.Length), Progress.bytes_read + buffer.Length, fulllength)
    End While
    RaiseEvent TextMessage("bytesRead = " + bytesRead.ToString)
    RaiseEvent TextMessage("postHeaderBytes.length = " + postHeaderBytes.Length.ToString)
    RaiseEvent TextMessage("Main File bytes written")
    ' Write out the trailing boundary
    requestStream.Write(boundaryBytes, 0, boundaryBytes.Length)
    Progress = New Progress_Struct(100 / fulllength * (Progress.bytes_read + boundaryBytes.Length), Progress.bytes_read + boundaryBytes.Length, fulllength)

    Dim responce As WebResponse
    Try
        RaiseEvent TextMessage("Trying normal response...")
        responce = webrequest__1.GetResponse()
    Catch ex As Net.WebException
        RaiseEvent TextMessage("ERROR " + ex.Status.ToString + " Trying to get Error response.")
        responce = ex.Response
    End Try

    Dim s As Stream = responce.GetResponseStream()
    Dim sr As New StreamReader(s)

    ContinueProgressLoop = False
    Dim returnstr As String = sr.ReadToEnd()
    'SendHtmlMessage(returnstr)

    RaiseEvent ProgressChange(Progress)
    RaiseEvent UploadFinished(returnstr)
    RaiseEvent TextMessage("Working FALSE")
    working = False
    Return returnstr
End Function

我希望任何人都可以帮助我解决这个问题。我不知道出了什么问题。

它应该返回带有重定向的“上传成功”消息,但它只返回上传页面本身。我检查了网络嗅探器。

【问题讨论】:

    标签: vb.net forms upload multipart form-data


    【解决方案1】:

    你为什么不看看System.Net.WebClient 类呢? http://msdn.microsoft.com/en-us/library/system.net.webclient.aspx 这样你就不用担心格式化POST数据了,你只需调用WebClient.UploadFile():http://msdn.microsoft.com/en-us/library/36s52zhs.aspx

    【讨论】:

    • 我试过这个。但是我必须传输多个帖子值,这意味着我必须传输多部分/表单数据。 UploadFile() 不支持这个。
    • 好的,现在我终于得到了这个问题。还有一件事——在阅读 responseStream 之前,您必须在 RequestStream() 上调用 close()。在我的回答中查看示例:*.com/questions/1948552/…
    • 我现在明白了。不过用了一段时间。我在请求的最后遗漏了一些“--”。比较浏览器的请求和我的请求!!我使用 Wireshark 解决了这个问题。
    • @sinni800 我遇到了类似的问题,我正在尝试使用 VB.NET 控制台应用程序上传 PDF。你能解释一下如何使用 Wireshark 查看 POST 数据吗?
    • 如果您还有更新的代码,您也可以发布它吗?
    最近更新 更多