Reputation: 55
I'm new working with HTTP protocol and haven't worked with VBS for some time. The problem I'm having is sending a parameter and an upload file to a web service. I just don't understand what some of the code is. Below is part of the code.
With CreateObject("MSXML2.ServerXMLHTTP")
.setOption 2, 13056 'http://msdn.microsoft.com/en-
us/library/ms763811(v=VS.85).aspx
.SetTimeouts 0, 60000, 300000, 300000
.Open "POST",
"https://192.168.100.100/api/import_file_here.json", False
.SetRequestHeader "Content-type", "multipart/form-data; boundary=" &
strBoundary 'THIS SEND THE FILE
.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded" '
THIS SEND THE PARAMETER.
.Send bytPD ' sends param
.Send bytPayLoad '''SEND FILE
I know I can't use .Send twice. I believe I need to make a change in the below code block.
With CreateObject("ADODB.Stream")
.Mode = 3
.Charset = "Windows-1252"
.Open
.Type = 2
.WriteText "--" & strBoundary & vbCrLf
'.WriteText "Content-Disposition: form-data; name=""file""; filename=""" &
strFile & """" & vbCrLf
.WriteText "Content-Disposition: form-data; name=""file"";
publication=""moveit_test_pub"""
'.WriteText "Content-Type: """ & strContentType & """" & vbCrLf & vbCrLf
.Position = 0
.Type = 1
.Write bytData
.Position = 0
.Type = 2
.Position = .Size
.WriteText vbCrLf & "--" & strBoundary & "--"
.Position = 0
.Type = 1
bytPayLoad = .Read
bytPD = "publication=moveit_test_pub"
bytPD = "publication=moveit_test_pub" is the parameter I need along with the file upload. I'm just not sure how to add it to the above block. If that's where I'm supposed to change. I'm posting the entire code below for reference. Thanks for all your help!
strFilePath = "C:\SCAudience_TEST5.txt"
UploadFile strFilePath, strUplStatus, strUplResponse
MsgBox strUplStatus & vbCrLf & strUplResponse
Sub UploadFile(strPath, strStatus, strResponse)
Dim strFile, strExt, strContentType, strBoundary, bytPD, bytData,
bytPayLoad
On Error Resume Next
With CreateObject("Scripting.FileSystemObject")
If .FileExists(strPath) Then
strFile = .GetFileName(strPath)
strExt = .GetExtensionName(strPath)
Else
strStatus = "File not found"
Exit Sub
End IF
End With
With CreateObject("Scripting.Dictionary")
.Add "txt", "text/plain"
.Add "html", "text/html"
.Add "php", "application/x-php"
.Add "js", "application/x-javascript"
.Add "vbs", "application/x-vbs"
.Add "bat", "application/x-bat"
.Add "jpeg", "image/jpeg"
.Add "jpg", "image/jpeg"
.Add "png", "image/png"
.Add "exe", "application/exe"
.Add "doc", "application/msword"
.Add "docx", "application/vnd.openxmlformats-
officedocument.wordprocessingml.document"
.Add "xls", "application/vnd.ms-excel"
.Add "xlsx", "application/vnd.openxmlformats-
officedocument.spreadsheetml.sheet"
strContentType = .Item(LCase(strExt))
End With
If strContentType = "" Then
strStatus = "Invalid file type"
Exit Sub
End If
With CreateObject("ADODB.Stream")
.Type = 1
.Mode = 3
.Open
.LoadFromFile strPath
If Err.Number <> 0 Then
strStatus = Err.Description & " (" & Err.Number & ")"
Exit Sub
End If
bytData = .Read
bytPD = "publication=moveit_test_pub"
End With
strBoundary = String(6, "-") & Replace(Mid(CreateObject("Scriptlet.TypeLib").Guid, 2, 36), "-", "")
With CreateObject("ADODB.Stream")
.Mode = 3
.Charset = "Windows-1252"
.Open
.Type = 2
.WriteText "--" & strBoundary & vbCrLf
' .WriteText "Content-Disposition: form-data; name=""file""; filename=""" & strFile & """" & vbCrLf
.WriteText "Content-Disposition: form-data; name=""file""; publication=""moveit_test_pub"""
'.WriteText "Content-Type: """ & strContentType & """" & vbCrLf & vbCrLf
.Position = 0
.Type = 1
.Write bytData
.Position = 0
.Type = 2
.Position = .Size
'' .WriteText vbCrLf & "--" & strBoundary & "--"
.Position = 0
.Type = 1
bytPayLoad = .Read
bytPD = "publication=moveit_test_pub"
End With
With CreateObject("MSXML2.ServerXMLHTTP")
.setOption 2, 13056 'http://msdn.microsoft.com/en-us/library/ms763811(v=VS.85).aspx
.SetTimeouts 0, 60000, 300000, 300000
.Open "POST", "https://192.168.100.100/api/import_file_here.json", False
.SetRequestHeader "Content-type", "multipart/form-data; boundary=" & strBoundary 'THIS SEND THE FILE IF BOTH SELECTED SEND PARM AND TEXT OF FILE
.SetRequestHeader "Content-Type", "application/x-www-form-urlencoded" ' THIS SEND THE PARAMETER.
''' .Send bytPD ' sends param
' .SetRequestHeader "Content-type", "multipart/form-data; boundary=" & strBoundary 'NEW LINE
.Send bytPayLoad '''SEND FILE
MsgBox bytPD
If Err.Number <> 0 Then
strStatus = Err.Description & " (" & Err.Number & ")"
Else
strStatus = .StatusText & " (" & .Status & ")"
End If
If .Status = "400" Then strResponse = .ResponseText
If .Status = "401" Then strResponse = .ResponseText
If .Status = "200" Then strResponse = .ResponseText
End With
End Sub
Upvotes: 2
Views: 4201
Reputation: 73
I found a solution. This was my logic:
with curl you can send a file + parameters with:
curl -XPOST '127.0.0.1:8000' -F 'file=@/Users/luca/Desktop/img.png' -F 'id=123456'
In this case you can see:
If you use netcat in listening mode like this...
nc -l -p 8000
This means that it's listening for anything on port 8000 of the localhost = 127.0.0.1 (I'm using the Mac version of Netcat. You may need to change some parameters to make it work like this).
So: launch netcat in listening mode, launch the previous curl command and you will see the entire POST packet. Now you know how it is made. It will look like that:
POST / HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: curl/7.54.0
Accept: */*
Content-Length: 427
Expect: 100-continue
Content-Type: multipart/form-data; boundary=------------------------60cd44468072da0e
--------------------------60cd44468072da0e
Content-Disposition: form-data; name="file"; filename="img.png"
Content-Type: application/octet-stream
?PNG
IHDR
??w&sRGB???gAMA??
?a pHYs??(J?IDAT(Scd``??D&(MU?
?bg?ܞ?IEND?B`?
--------------------------60cd44468072da0e
Content-Disposition: form-data; name="id"
123456
--------------------------60cd44468072da0e--
Now that you know how the working packet is made, you can replicate it.
For the header use:
httpServer.SetRequestHeader "Content-type", "multipart/form-data; boundary=------------------------2deddc24cb2a8ca2;"
(boundary is sort of delimiter. Check it on Google)
Then you can build the body of the POST request:
body = "--" & "------------------------2deddc24cb2a8ca2" & vbCrLf & _
"Content-Disposition: form-data; name=""file""; filename=""" & objFSO.GetFileName(objFile) & """" & vbCrLf & _
"Content-Type: application/octet-stream" & vbCrLf & vbCrLf & _
FILE_CONTENT & vbCrLf & _
"--" & "------------------------2deddc24cb2a8ca2" & vbCrLf & _
"Content-Disposition: form-data; name=""id""" & vbCrLf & vbCrLf & _
ID & vbCrLf & _
"--" & "------------------------2deddc24cb2a8ca2" & "--" & vbCrLf & vbCrLf
NOTE:
PROBLEM: The code you posted below should open a stream, write the first part of the body as a string, write the BINARY content of your file, write the last part of the body as a string.
Combining String and Binary data it's not working for me: i can send only the binary or only text file. If i convert the binary content to string, the remote server will get a corrupted (different) file...
example (binary file only):
Set stream = CreateObject("ADODB.Stream")
stream.Mode = 3
stream.Type = 1
stream.Open
stream.LoadFromFile("C:\Users\Luca\Desktop\i.png")
Set objHttp = CreateObject("MSXML2.ServerXMLHTTP")
objHttp.Open "POST", "http://10.0.2.2:8000/", False
objHttp.Send stream.Read(stream.Size)
example (text file only)
Set stream = CreateObject("ADODB.Stream")
stream.Mode = 3
stream.Type = 2
stream.Open
stream.LoadFromFile("C:\Users\Luca\Desktop\i.txt")
readBinaryFile = stream.Read
requestBody = "--------------------------2deddc24cb2a8ca2" & vbCrLf & _
"Content-Disposition: form-data; name=""file""; filename=""" & objFSO.GetFileName(objFile) & """" & vbCrLf & _
"Content-Type: application/octet-stream" & vbCrLf & vbCrLf & _
readBinaryFile & vbCrLf & _
"--------------------------2deddc24cb2a8ca2" & vbCrLf & _
"Content-Disposition: form-data; name=""id""" & vbCrLf & vbCrLf & _
ID & vbCrLf & _
"--------------------------2deddc24cb2a8ca2--" & vbCrLf & vbCrLf
As i told you, if you change the stream.Type from 2 to 1 (for Binary) you will end to send a corrupted file.
My solution was to send the parameter as an extra Header value: Example:
httpServer.Open "POST", "http://10.0.2.2:8000/", False
httpServer.SetRequestHeader "Content-type", "application/octet-stream;"
httpServer.SetRequestHeader "Id", ID
httpServer.Send stream.Read(stream.Size)
Now i can send the parameter (Id) AND the binary file...
NOTE: With Content-type: application/octet-stream you can send unknow file extension too
Upvotes: 4