user3641694
user3641694

Reputation: 55

send a file and parameter HTTP Post with VBS

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

Answers (1)

Luca Reggiannini
Luca Reggiannini

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:

  • IP = 127.0.0.1 (localhost)
  • Port = 8000
  • Filename = img.png
  • Parameter = "id" with value 123456

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:

  • in the body you can see that every boundary has an additional "--" string at the beginning (infact i wrote "--" & "------------------------2deddc24cb2a8ca2") and an additional "--" at the end of the last boundary.
  • The header must have ";" at the end of the line in vbs even if is not visible in the previous captured POST request. I don't know exactly why.
  • The FILE_CONTENT variable in the body is the content of your file
  • Take care for every vbCrLf (end of the line) or the POST request may not be valid.

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

Related Questions