Uploading files via a web or a mobile app is a common use case in Visual Builder.  In previous posts, we have discussed upload via REST APIs using different payloads like application/x-www-form-urlencoded, multipart/form-data and application/octet-stream. In this post, we discuss some more upload use cases in the multipart/form-data category that we have seen VB developers sometimes struggle with getting right.  

Browsers implement a nifty FormData interface that helps you quickly and easily form the payload of multipart/form-data REST APIs.  All you have to do is the below :
    let formData = new FormData();
    formData.append("sampleString",stringValue);
    formData.append("sampleFile",blobValue);

 

FormData only accepts String or Blob fields; if the sent value is anything other than these, it is automatically converted into a string. A blob can be used to represent files with the required content-type (or MIME type).  It is important to remember that strings dont have any content-type, since they are just plain text.  Therefore, whatever be your multipart/form-data REST API, it should have only two field types: String or Blob (or File).

In a VB app, whenever we assign an object to the body of a Service Connection representing a multipart/form-data REST API, each field of the object becomes an individual "part" of the multipart payload.  For example in the image below, assigning sampleBody variable to the body of the multipart/form-data based Service Connection is equivalent to construction of a FormData with the individual parts set to the sampleBody properties.

 

Thus in VB you have the flexibility to do both :

  • Via object assignment – Use an object to represent the individual parts of the payload and assign it directly to the body
  • Via FormData – Construct the FormData object yourself in JavaScript and assign it to the body

We discuss a variety of file upload scenarios in VB using the second option (via FormData) , however the first option can also be used.  We will use the same steps for each of these scenarios and only differ in the population of the FormData object

Common Steps for setting up a sample app for multipart upload

  1. The multipart/form-data REST API is modelled as a Service Connection.  The Media Type is set as multipart/form-data in the Request Body, and a sample Response Body is given.  Note that you cannot test such an API in the Service Tester.  For a step by step setup of such an API, refer to the previous blog.  Shown below is the setup of such a Service Connection.

     

  2. In the application page, we have Input Text elements for each of the string form-data parts and File Picker elements for each of the File form-data parts.

     

  3. Each Input Text element is mapped to a variable of type String

     

  4. Each File Picker element has its ojSelect event map the selected file to a variable of type Any

     

  5. The page has a button, which when clicked, calls the Action Chain, which is responsible to convert the variables into multipart payload and then call the multipart/form-data REST API with this payload.  The payload creation is done via a PageModule JavaScript function (in my case called createBody).  In all the use cases outlined in the next section, we will vary the code of the createBody function to accomodate for different REST API payloads

     

 

Multipart/form-data usecases

Case 1 – REST API that accepts two files of two different mime types

Our REST API accepts two files for upload of two different mime types – "file1" and "file2".  Therefore we would be having two File Picker elements (or alternatively multiple files selected from the same File Picker).In this case our createBody function will accept the two variables (of type Any) corresponding to the two files and construct the FormData object based on the same

  /**
   *
   * @param {File} sampleFileField - First file
   * @param {File} sampleFileField2 - Second file
   * @return {FormData} 
   */
    PageModule.prototype.createBody = function (sampleFileField, sampleFileField2) {
    let formData = new FormData();
    formData.append("file1",sampleFileField);
    formData.append("file2",sampleFileField2);
    return formData;
  };

Case 2 – REST API that accepts one file and one text field 

Here, our multipart REST API has a file field called "uploadFile", and a text field called "fileName".  The createBody function would look like the code below.

  /**
   *
   * @param {File} sampleFileField - file
   * @param {String} sampleTextField - plain text
   * @return {FormData} 
   */
    PageModule.prototype.createBody = function (sampleFileField, sampleTextField) {
    let formData = new FormData();
    formData.append("uploadFile",sampleFileField);
    formData.append("fileName",sampleTextField);
    return formData;
  };

Case 3 – REST API that accepts one file and one field with application/json content-type

Recall that the FormData interface can only have string or Blob based fields.  The string field doesnt have a content-type.  Therefore, the only option to send a JSON text with a content-type of application/json would be to send a JSON File or Blob with the right mime type in the field.  The createBody function would look like the code below.

  /**
   * @param {File} sampleFileField - file
   * @param {String} sampleTextField - json text
   * @return {FormData} 
   */
  PageModule.prototype.createBody = function (sampleFileField, sampleTextField) {
    let formData = new FormData();
    let jsonData = new Blob([JSON.stringify(sampleTextField, null, 2)], { type: "application/json" });
    formData.append("uploadFile", sampleFileField);
    formData.append("jsonData", jsonData);
    return formData;
  };

Case 4 – REST API that accepts an array of two files

This is rare case, however some REST APIs might accept an array of files.  In this case, we simply append twice to the FormData object with the same field name.  Example code below:

  /**
   * @param {File} sampleFileField - First file
   * @param {String} sampleFileField1 - Second file
   * @return {FormData} 
   */
  PageModule.prototype.createBody = function (sampleFileField, sampleFileField1) {
    let formData = new FormData();
    formData.append("fileArray", sampleFileField, "file1.jpg"); 
    formData.append("fileArray", sampleFileField1, "file2.jpg");
    return formData;
  };

 

In conclusion, the payload of any REST API accepting multipart/form-data can be constructed in this way.  That completes this post – thanks for reading!