File upload in Javascript using fetch

In this article we'll see how to upload a file using the fetch API. We'll take a PHP example and update it, in order to improve user experience.

The code is available here.

đź“– This is a translation from this article. So there is some French in code in order to keep consistency with demonstrations GIFs

Upload in PHP

Let's take the following example :

upload-php

After the form submission, we see that the URL is changing to upload.php. This page store the uploaded file and display a success message. Finally the user is redirected to the first page. The example work but it require three pages loads.

Let's see the code.

<!DOCTYPE html>
<html>
  <head>
    <!-- ... -->
  </head>
  <body>
    <form action="upload.php" method="post" enctype="multipart/form-data">
      <input type="file" name="background" />
      <button type="submit">Envoyer</button>
    </form>
  </body>
</html>

The HTML code is very common. We only have to pay attention to the enctype attribute. The value must be multipart/form-data in order to do a file upload.

<?php
    header("Access-Control-Allow-Origin: *");

    /* The superglobal variable $_FILES gives us the ability to access
    all files that were uploaded using an HTML form. The background key
    makes reference to the value of the name attribute in
    <input name="background" /> */
    $file = $_FILES["background"];
    $isFileUploaded = move_uploaded_file($file["tmp_name"], __DIR__ . "/../storage/" . $file["name"]);

    if($isFileUploaded === false) {
        http_response_code(500);
        echo "Problème serveur";
    }
    else {
        http_response_code(201);
        readfile('success.html');
    }

    // Redirection on index.html after 2 seconds
    header("Refresh:2; url=index.html", true, 303);

The server side retrieve the file and move it into the storage folder. When processing is over, we display a success message and we redirect him to the index.html page after two secondes. The main point is that the name given to the input element is important, because it's this name that the server is using to retrieve the file.

Web APIs

We'll need two web API to perform the Upload in Javascript : FormData and Fetch.

FormData

Form Data

FormData is well supported by browsers.

This API gives us the ability to represent a form in Javscript. This form will have a multipart/form-data encoding, so it's not necessary to precise it in the HTML. As already mentioned, this encoding is necessary when you're uploading a file.

Fetch

Fetch

Surprisingly, we have compatibilities problems with Internet Explorer. There are two solution to avoid this :

  • Use a polyfill
  • The trick I'm will use later in the article

The fetch API is the modern way to perform HTTP requests. It's based on promise. There are various syntaxes for using promises, here we'll use the On peut utiliser les promesses avec plusieurs syntaxe, ici on utilisera async/await.

/* The async keyword means that the await keyword
will be used inside the function. */
async function getLucky() {
  /* The await keyword means that we're wating the results of a
   function, here somethingThatTakesTime. We don't know when the
   function will return a value, so we're awaiting the end of the
   process before executing next instructions. */
  const luck = await somethingThatTakesTime()

  return luck
}

I explained it in a short way, because it's not the main subjects. You'll be able able to easily find articles that are talking about promises and async/await.

In Javascript

<!DOCTYPE html>
<html>
  <head>
    <!-- ... -->
  </head>
  <body>
    <form id="form">
      <input type="file" name="background" />
      <button type="submit">Envoyer</button>
    </form>
    <p id="message"></p>
    <script src="./app.js"></script>
  </body>
</html>

The HTML part is mainly the same. Previous form's attributes have been replaced by an id. And we're adding a paragraph where we'll display the success or failure message. And finally we load our script.

/* We get form and message elements */
const form = document.getElementById("form")
const message = document.getElementById("message")

/* We're listing for the form submission and
we're preventing the default behaviour */
form.addEventListener("submit", async function (e) {
  e.preventDefault()

  /* If your not using a polyfill for fetch, the trick for IE is to
  suggest the user to use an other browser. */
  if (!window.fetch || !window.FormData) {
    alert("R u fucking kidding me ??? Use another browser right now !")
    return
  }

  /* When we're instanciating FormData, we can pass it a form element.
  FormData will be able to detect all inputs of the form and their
  values. */
  const formData = new FormData(form)

  try {
    /* fetch() is taking two parameters, the first is URL and
    the second are options. Here we're telling fetch that we'll
    make a POST request and the body of the request, the data
    we're sending will be our formData */
    await fetch("http://localhost:4000/upload.php", {
      method: "POST",
      body: formData,
    })

    // We display a success or failure message.
    message.innerText = "Fichier uploadé avec succès \\o/"
  } catch (error) {
    message.innerText = "Il y a eu un problème /o\\"
  }

  // We clean the form
  form.reset()

  // We're removing the message after two seconds
  setTimeout(() => {
    message.innerText = ""
  }, 2000)
})

With this code, we get this result.

upload-js

The last step is server optimization. You can remove these lines.

<?php
    header("Access-Control-Allow-Origin: *");

    $file = $_FILES["background"];
    $isFileUploaded = move_uploaded_file($file["tmp_name"], __DIR__ . "/../storage/" . $file["name"]);

    if($isFileUploaded === false) {
        http_response_code(500);
-       echo "Server issue";
    }
    else {
        http_response_code(201);
-       readfile('success.html');
    }

-   header("Refresh:2; url=index.html", true, 303);

Conclusion

Combination of FormData and fetch APIs are making file upload really easy. We're avoiding pages reload in order to improve user experience.

To perform HTTP requests, it's possible, like in this post, to use fetch. But you should consider :

  • Axios, XMLHttpRequest based library. XMLHttpRequest is the old way to perform HTTP request, so it's compatible with IE.
  • Ky, a wrapper for fetch which add some features. Ky compared to axios by Ky's creator.

Finally, it's possible to use these principles with libraries such as React or Vue.js. You'll just have to use references in order to access form element.

Thanks for reading.

Maxime Blanc


© 2020 Maxime Blanc. Tous droits réservés.