Usually while building web applications, we find ourselves in a situation where we need to deal with uploading files. So we build a form with enctype="multipart/form-data", and an input tag with file type.Plain simple, and we are ready to go. However, when building web APIs, especially ones that utilize JSON only, then uploading images is a bit different.

Two Phases

Now one way to deal with this is to send data separately on multiple requests. For instance, let's assume that our initial purpose is that we want to send a POST request that contains the data of a potential user for registration which requires a profile picture to be addressed as well.

What a two phases option suggests is that we send a POST request to an endpoint so first, we save the data of the user which returns the newly created user ID for instance, then another request to upload profile image right after the first request finish.

The first request could look something like the following.

POST http://example.com/api/V1/user

{
    "name": "John Doe",
    "email": "john.doe@email.com",
    ...
}

Then a returned id for the user or even an URL that the client can post the image to is also an option.

{
    "created_user_id": 4356,
    "post_image": "http://uploads.example.com/image/user/4356",
    ...
}

The advantage of taking this approach is that when sending a significant amount of data, splitting it into multiple requests is proper way to handle this especially if the second request that uploads the image is pointing to another server.

Base64 Encode

Sometimes sending multiple requests is not necessary and not recommended, especially in our previous case where the amount of data is small and intact. In that case, it would be better to approach the problem in another way; Base64 encode the file. When a file gets encoded, we can directly just send it as text, and on the other side, the back-end, for instance, we would decode it and save it the disk for example. Let's write an example in JavaScript that encodes an image from an input and see how does that look.

First, let's build the HTML. We need an input with type="file" to choose a file, a button to trigger the conversion to base64, and optionally an img tag to display the image.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>File to Base64</title>
</head>
<body>  
  <input id="file" type="file"/>
  <button id="convert">Convert</button>
  <br>
  <img id="source" src="" alt="" width="200" height="200">
</body>
</html>

Then the JavaScript piece of code will look like the following.

document
  .getElementById('convert')
  .addEventListener('click', () => {
    let files = document.getElementById('file').files;
    if (files.length > 0) {
      getBase64(files[0]);
    }
  });

function getBase64(file) {
  let reader = new FileReader();
  
  reader.readAsDataURL(file);

  reader.onload = function () {
    let img = document.getElementById('source');
    img.src = reader.result;
    console.log(reader.result)
  };
}

First, we add a click event listener on the button where we check if there is a file that has been selected, and if so, we call getBase64 on the file. The getBase64 function uses the FileReader class which has the readAsDataURL() that read the contents of the file. The onload property contains event handler which gets executed when the load event is fired. We load the base64 encoded image to the src of img on the HTML. We are also console.log the encoded data to see how it looks like.

The example above is meant for demonstration only, and it will need more handling in a real-world scenario.

Here is a working version of the example on codepen.