The CreatorCon Call for Content is officially open! Get started here.

REST Multipart/form-data binary image request body?

Nick65
Mega Expert

So I have been working on an integration and was curious if anyone has successfully created a multipart/form-data outbound REST request with binary image data in Servicenow? With or without use of mid server. I know the request works because it works fine in Fiddler and Restify. Below is the body I am using, I have been able to post this exact message with a text file so I know the issue is with the binary I am sending. I have also tried base64. Does anyone have any tips or tricks for sending binary image data with Javascript? Any pitfalls or issues you have ran into in the past? Thanks !

--gc0p4Jq0M2Yt08jU534c0p

Content-Disposition: form-data; name="body"


goes with message

--gc0p4Jq0M2Yt08jU534c0p

Content-Disposition: form-data; name="attachment1"; filename="testpic.gif"

Content-Type:image/gif

Content-Transfer-Encoding: binary


${attachment}

--gc0p4Jq0M2Yt08jU534c0p--

1 ACCEPTED SOLUTION

Yes, I finally got this done with a custom .jar file and Javascript probe. I wrote a reply on


JavascriptProbe and MID Server Script Includes-John Andersen


I believe you must be the same Jonathan, let me know if you have any questions.


View solution in original post

15 REPLIES 15

Sreeja Gattu
Kilo Expert

Hi Nick,


We are doing Integration between ServiceNow and JIRA where we need to send attachments to JIRA using REST API.


So, I wrote a Mid Server Script Include which contains the code as below.



var pathname= "/nav_to.do?uri=sys_attachment.do?sys_id=f646a0df4f0a020079b94fe18110c79c";


var fileUpload = new Packages.java.io.File(pathname);


var httpClient = new Packages.org.apache.commons.httpclient.HttpClient();


var postRequest = new Packages.org.apache.commons.httpclient.methods.PostMethod("----------------------------");


  postRequest.setHeader("Authorization", "Basic");


postRequest.setHeader("username", "------");


  postRequest.setHeader("password", "--------");


postRequest.setHeader("X-Atlassian-Token","nocheck");


var parts = new Packages.org.apache.commons.httpclient.methods.multipart.FilePart(fileUpload.getName(),fileUpload);


var entity=new Packages.org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity(parts,postRequest.getParams());


  postRequest.setRequestEntity(entity);


var response = httpClient.execute(postRequest);


var responseBody = response.getBody();


  var httpStatus = response.getStatusCode();



Issue is:


While I try to execute this using Javascript Probe, Iam getting error at


var parts = new Packages.org.apache.commons.httpclient.methods.multipart.FilePart(fileUpload.getName(),fileUpload);


Error is "java.io.FileNotFoundException: File is not a normal file".




Could you please help me on this in knowing what is the mistake.




Thanks in advance


Sreeja


Ok looks a little different than what I did, but I will tell you what I see that is different.


For the first two lines.


var pathname= "/nav_to.do?uri=sys_attachment.do?sys_id=f646a0df4f0a020079b94fe18110c79c";


var fileUpload = new Packages.java.io.File(pathname);


I didn't pass in the files this way, I ended up converting the Base64 string to a byte array and then attaching it to the request. I am no Java expert but I am not sure if the java.io.File object in java takes a base64 string as an argument does it? Because remember files are stored as Base64 in ServiceNow.


I did something like this.


import org.apache.commons.codec.binary.Base64




byte [] decodedBytes = (Base64.decodeBase64(b64string.getBytes()));


String message = "New File attached";




Part[] parts ={


new StringPart("body", message),


new FilePart("atttachment1", new ByteArrayPartSource(filename,decodedBytes), contentType, null)


};




Also another thing that you might need to look   at is the boundary and content type. Again I am not an expert in HTTP but I know the boundary header/parameter is required when building multipart requests so you may want to look at that too.




I set it like this.


postRequest.setRequestHeader("Content-Type", "multipart/form-data;boundary=" + boundary);






I honestly didn't have any luck sending a base64 string in a REST request. I would try this if you are not having luck with it either. Let me know if that helps at all.


Hello Nick,



Can you provide some code/jar from your solution. I'm doing something similar, this would be of great help.


I was able to accomplish this by using a MID Server Script include and using these functions, see if they help you:



_getURLConnection: function(url) {


              if (ms.getConfigParameter("mid.proxy.use_proxy") == 'true') {


                      Packages.java.lang.System.setProperty("https.proxyHost", ms.getConfigParameter("mid.proxy.host"));


                      Packages.java.lang.System.setProperty("http.proxyHost", ms.getConfigParameter("mid.proxy.host"));


                      Packages.java.lang.System.setProperty("https.proxyPort", ms.getConfigParameter("mid.proxy.port"));


                      Packages.java.lang.System.setProperty("http.proxyPort", ms.getConfigParameter("mid.proxy.port"));


              }


              var conn = new Packages.java.net.URL(url).openConnection();




              this.logMsg("Sending to : " + url, "debug");




              var userpass = new Packages.java.lang.String(this.probeParameters.username + ":" + this.probeParameters.password);


              conn.setRequestProperty("X-Atlassian-Token", "nocheck");


              conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + this.boundary);


              var basicAuth = "Basic " + (new Packages.javax.xml.bind.DatatypeConverter.printBase64Binary(userpass.getBytes()) + '');


              conn.setRequestProperty("Authorization", basicAuth);


              conn.setDoOutput(true);


              conn.setDoInput(true);


              conn.setRequestMethod("POST");


              conn.setUseCaches(false);


              return conn;


      },




      _getFile: function() {


              //Get The File as Base64 and convert to ByteArrayInputStream


              return new Packages.com.glide.util.StringUtil.base64DecodeAsBytes(this.probeParameters.encpayload);


      },




      _writeFile: function(conn, uploadFile) {


              var outputStream = conn.getOutputStream();


              var writer = new Packages.java.io.PrintWriter(new Packages.java.io.OutputStreamWriter(outputStream, this.charset), true);


              var fieldName = 'file'; // JIRA Requires this as 'file'


              var fileName = this.probeParameters.fileName;




              this.logMsg("Sending file : " + fileName, "debug");




              writer.append("--" + this.boundary).append(this.LINE_FEED);


              writer.append("Content-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"").append(this.LINE_FEED);


              //Sometimes ServiceNow does not have the content type for a file (attempt to get it, otherwise default to octet-stream):


              if (JSUtil.notNil(this.probeParameters.contentType)) {


                      writer.append("Content-Type: " + this.probeParameters.contentType).append(this.LINE_FEED);


              } else if (JSUtil.notNil(Packages.java.net.URLConnection.guessContentTypeFromName(fileName))) {


                      writer.append("Content-Type: " + Packages.java.net.URLConnection.guessContentTypeFromName(fileName)).append(this.LINE_FEED);


              } else {


                      writer.append("Content-Type: application/octet-stream").append(this.LINE_FEED);


              }


              writer.append("Content-Transfer-Encoding: binary").append(this.LINE_FEED);


              writer.append(this.LINE_FEED).flush();




              this._writeFileData(uploadFile, outputStream);




              writer.append(this.LINE_FEED).flush();


              writer.append(this.LINE_FEED).flush();


              writer.append("--" + this.boundary + "--").append(this.LINE_FEED);


              writer.close();


              var readResponse = "";


              if (conn.getResponseCode() == 200) {


                      var reader = new Packages.java.io.BufferedReader(new Packages.java.io.InputStreamReader(conn.getInputStream()));


                      var line = reader.readLine();


                      while (line != null) {


                              readResponse += line;


                              line = reader.readLine();


                      }


              }


              return conn.getResponseCode();


      },






      _writeFileData: function(uploadFile, outputStream) {


              var inputStream = new Packages.java.io.ByteArrayInputStream(uploadFile);




              var data = new Packages.java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);


              var bytesRead = 0;


              while ((bytesRead = inputStream.read(data)) != -1) {


                      outputStream.write(data, 0, bytesRead);


                      outputStream.flush();


              }


              inputStream.close();


      },


Thanks Jonathan