Help on VBA program to add attachment to a confluence server page
Asked by Rosario De Domenico:

Hi All,

I have used the code given [in this answer](https://answers.atlassian.com/questions/39127148/help-on-vba-program-to-add-an-attachment-to-a-jira-issue-using-api2issueissueidorkeyattachments) for JIRA as a starting point but I do not manage to get through...

What I want to do is to add (and / or update) an attachment sitting on my client laptop to a confluence page in our server instance using VBA code in an Excel file.

Below the code up to the last point where I got and the response text that I get back.

```
Sub rdd_sendAttachementToConfluencePagebyID()??? Dim httpRestAPIreq As New WinHttpRequest??? Dim strRequestBodyStart, strRequestBodyEnd, strPassword, strUsername, strBoundary, strBaseURL, strFullURL, strPageID, strFilePath, strFileName, strFileData As String??? ??? strFilePath = "C:\folder1\fodler2\file.png"??? strPageID = "7340078"??? strBaseURL = "http://theserver:8090"??? strFullURL = strBaseURL &amp; "/rest/api/content/" &amp; strPageID &amp; "/child/attachment"??? strBoundary = "---------------------------7e0139a150b4e"??? strUsername = "id"??? strPassword = "pwd"???? strRequestBodyStart = "--" &amp; strBoundary &amp; vbCrLf &amp; _????????????????????????? "Content-Disposition: form-data; name=""file_0"";value = """ &amp; strFilePath &amp; """;" &amp; vbCrLf &amp; _????????????????????????? "Content-Type: application/octet-stream" &amp; vbCrLf &amp; vbCrLf???? strRequestBodyEnd = vbCrLf &amp; vbCrLf &amp; "--" &amp; strBoundary &amp; "--"???? With httpRestAPIreq??????? .Open "POST", strFullURL, False??????? .setRequestHeader "X-Atlassian-Token", "no-check"??????? .setRequestHeader "Content-Type", "multipart/form-data; boundary=" &amp; strBoundary??????? .setRequestHeader "Authorization", "Basic " &amp; rdd_encodeBase64(strUsername &amp; ":" &amp; strPassword)??????? .send StrConv(strRequestBodyStart, vbFromUnicode) &amp; rdd_getFileAsBinary(strFilePath) &amp; StrConv(strRequestBodyEnd, vbFromUnicode) 'Converts from string to bytes??????? Debug.Print .responseText??? End WithEnd SubFunction rdd_encodeBase64(strStrinToEncode As String) As String??? Dim arrData() As Byte??? Dim objXML As New MSXML2.DOMDocument??? Dim objNode As MSXML2.IXMLDOMElement??? arrData = StrConv(strStrinToEncode, vbFromUnicode)??? Set objNode = objXML.createElement("b64")??? objNode.DataType = "bin.base64"??? objNode.nodeTypedValue = arrData??? rdd_encodeBase64 = objNode.Text??? Set objNode = Nothing??? Set objXML = NothingEnd FunctionFunction rdd_getFileAsBinary(ByVal strPath As String) As Variant??? Dim buffer() As Byte??? Dim lngLogFileHdl As Long??? lngLogFileHdl = FreeFile??? Open strPath For Binary As #lngLogFileHdl??? ReDim buffer(1 To LOF(lngLogFileHdl))??? Get #lngLogFileHdl, , buffer??? Close #lngLogFileHdl??? rdd_getFileAsBinary = bufferEnd Function
```

I get back:

```
{"statusCode":400,"data":{"authorized":false,"valid":true,"errors":[],"notSuccessful":true,"successful":false},"message":"At least one attachment file must be included."}
```

Can someone help me pointing out what I am doing wrong?


Answers:
author: Rosario De Domenico
body: Following the request in the comment I expand how I did it here, but I followed a different path therefore I did not really answer the original request.

I downloaded a compiled *curl.exe* for Windows (curl_7_50_3_openssl_nghttp2_x64.7z) unfortunately I cannot find the link any more, but it was github, sourceforge or something similar.

I downloaded the VBA-JSON-2.2.1 library from github.

I implemented the following logic in VBA:

* build up the REST API URL in a string based on which action you want to perform (fetch a page ID, fetch page content, add or update an attachment, etc.); refer to Confluence documentation: <https://docs.atlassian.com/atlassian-confluence/REST/latest-server/>
* build up the CURL command in another string based on Confluence documentation and desired output / format; refer to CURL documentation (i.e. man page, do not remember the link but easy to find)
* launch the curl.exe with proper command option and URL
* use / parse the output as required.

In VBA I separated the constants (like URL path) and split the actions in multiple functions for reuse; here a couple of such functions as examples. Please, note that a couple of extra libraries need to be referenced (not sure anymore which ones, probably Microsoft Script Control 1.0, Windows Script Host Object Model).

```
Private Function rdd_getConfluencePageIDbyTitle(strUsID As String, strPWD As String, strServerURL As String, strSpaceKey As String, strPageTitle As String) As String'PRE: following global constant must exist and be defined: strCURLexecutable As String --&gt; the full path and name of the curl executable??? Dim strFullURL As String??? Dim strCURLoptions As String??? Dim strFullJSONresponse As String??? Dim strHTTPstatus As String??? Dim strSize As String??? Dim shlCmdPrpt As New WshShell??? Dim shlOutput As WshExec??? strFullURL = strServerURL &amp; "/rest/api/content?spaceKey=" &amp; strSpaceKey &amp; "&amp;title=" &amp; rdd_URLencode(strPageTitle)??? strCURLoptions = "-w ""\n%{http_code}"" -u " &amp; strUsID &amp; ":" &amp; strPWD??? Set shlOutput = shlCmdPrpt.Exec(strCURLexecutable &amp; " " &amp; strCURLoptions &amp; " """ &amp; strFullURL &amp; """")??? strFullJSONresponse = shlOutput.StdOut.ReadLine??? strHTTPstatus = shlOutput.StdOut.ReadAll??? If strHTTPstatus &lt;&gt; "200" Then??????? MsgBox "Could not get page ID by title, HTTP status: " &amp; strHTTPstatus &amp; "."??????? rdd_getConfluencePageIDbyTitle = "-1"??????? Exit Function??? End If??? strSize = JsonConverter.ParseJson(strFullJSONresponse).Item("size")??? Select Case strSize??????? Case "0"??????????? MsgBox "Could not get page ID; no page found with title: " &amp; strPageTitle &amp; "."??????????? rdd_getConfluencePageIDbyTitle = ""??????? Case "1"??????????? rdd_getConfluencePageIDbyTitle = JsonConverter.ParseJson(strFullJSONresponse).Item("results")(1).Item("id")??????? Case Else??????????? MsgBox "Could not get page ID; found: """ &amp; strSize &amp; """ pages with title: " &amp; strPageTitle &amp; "."??????????? rdd_getConfluencePageIDbyTitle = "-1"??? End SelectEnd Function?Private Sub rdd_addAttachementToConfluenceByID(strUsID As String, strPWD As String, strServerURL As String, strPgID As String, strFileURI As String)'PRE: following global constant must exist and be defined: strCURLexecutable As String --&gt; the full path and name of the curl executable??? Dim strFullURL As String??? Dim strCURLoptions As String??? Dim strHTTPstatus As String??? Dim shlCmdPrpt As New WshShell??? strFullURL = strServerURL &amp; "/rest/api/content/" &amp; strPgID &amp; "/child/attachment"??? strCURLoptions = "-o nul -w ""%{http_code}"" -u " &amp; strUsID &amp; ":" &amp; strPWD &amp; " -X POST -H ""X-Atlassian-Token: nocheck"" -F ""file=@" &amp; strFileURI &amp; """ -F ""comment=Added by VBA macro through REST API."""??? strHTTPstatus = shlCmdPrpt.Exec(strCURLexecutable &amp; " " &amp; strCURLoptions &amp; " " &amp; strFullURL).StdOut.ReadAll??? If strHTTPstatus &lt;&gt; "200" Then??????? MsgBox "Could not attach file: " &amp; strFileURI &amp; " to confluence page, HTTP status: " &amp; strHTTPstatus &amp; "."??? End IfEnd Sub
```


author: Javier Medina
body: Hi Rosario,

Is there any way you can share your curl solution? Thank you


author: Rosario De Domenico
body: At the end I resolved to use a curl executable and invoke that one from my VBA macro.

This worked perfectly.


Tags: confluence-administration