VB.NET - o upload de formulário multiparte não funciona.O que está errado?
Pergunta
Tentei pesquisar e descobri que alguém escreveu um uploader de formulário multiparte.
Embora tivesse uma coisa errada:Ele transferiu todos os - supostamente - valores POST no URL.O que o aplicativo da web não gosta - de alguma forma.
Foi tudo
POST /index.php?page=post&s=add&title=test&tags=testtags HTTP/1.1
E apenas adicionei o arquivo ao conteúdo da postagem.
Tentei enviar com um navegador e saiu o seguinte:
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--
Substituí os bytes de dados da imagem por IMGDATA para torná-los mais curtos.
Este upload foi aceito.Então tentei imitar isso com o código VB.NET que mostrarei em breve.Isso saiu:
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
Isso NÃO foi aceito.Então qual foi o problema?Também tentei colocar os dois extras (--) em outro lugar.E no final, não mudou nada.Este é o recorte do código VB.NET que usei para transmissão.O Uploader.Start inicia o processo de upload através de um thread que utiliza as variáveis globais.
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
Espero que alguém possa me ajudar com isso.Não consigo descobrir o que há de errado.
Ele deve retornar uma mensagem "Sucesso no upload" com um redirecionamento, mas retorna apenas a própria página de upload.Verifiquei com um sniffer de rede.
Solução
Por que você não dá uma olhada System.Net.WebClient
classe em vez disso? http://msdn.microsoft.com/en-us/library/system.net.webclient.aspx Desta forma você não precisa se preocupar em formatar os dados do POST, basta chamar WebClient.UploadFile()
: http://msdn.microsoft.com/en-us/library/36s52zhs.aspx