Uploader un fichier en Javascript avec fetch
Dans cet article on va voir comment uploader un fichier en utilisant Javascript et l’API fetch. On partira d’un exemple en PHP que l’on fera évoluer pour atteindre le résultat souhaité.
Le code complet est disponible ici.
Version PHP
Considerons l’exemple suivant :
Après la soumission du formulaire, on voit que l’URL change vers upload.php
. Cette page enregistre le fichier uploadé et affiche un message de succès. Il est ensuite redirigé vers la page index.html
. Ce système fonctionne, mais il implique trois chargements de pages.
Regardons le code utilisé.
<!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>
Ce code HTML est standard. Le seul point d’attention est l’attribut enctype dont la valeur doit obligatoirement être multipart/form-data pour pouvoir uploader un fichier.
<?php
header("Access-Control-Allow-Origin: *");
/* La variable superglobale $_FILES nous donne accès aux fichiers
qui ont été uploadés. La clé "background" fait référence à
l'attribut name de <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 sur index.html après 2 secondes
header("Refresh:2; url=index.html", true, 303);
La partie serveur récupère le fichier et de le déplace dans le dossier storage. Une fois le traitement terminé on affiche un message à l’utilisateur puis on le redirige vers la page index.html
après deux secondes. L’essentiel étant de retenir que le nom donné à l’input à son importance et sera utilisé par le code PHP pour récupérer le fichier.
APIs web
Nous allons avoir besoin de deux API web : FormData et Fetch.
FormData
FormData dispose d’un très bon support de la part des navigateurs.
Cette API permet de représenter un formulaire côté Javascript. Ce formulaire aura un encodage de type multipart/form-data, donc il n’est pas nécessaire de le préciser dans le HTML. Ce type d’encodage est nécessaire dès lors que l’on souhaite uploader un fichier.
Fetch
Aussi surprenant que cela puisse paraître, c’est Internet Explorer qui nous casse les ligaments. Il existe deux solutions pour contourner le problème de compatibilité :
- Utiliser un polyfill
- L’astuce que je présente plus bas
L’API fetch est la façon moderne pour faire des requêtes HTTP. Elle repose sur les promesses (Promise). On peut utiliser les promesses avec plusieurs syntaxes, ici, on utilisera async/await.
/* Le mot-clé async signifie que le mot clé await va être
utilisé dans le corps de la fonction */
async function getLucky() {
/* Le mot clé await signifie que l'on attend le resultat d'une
fonction, ici somethingThatTakesTime. On ne sait pas combien
de temps mettra la fonction à répondre donc on attend le resultat
avant d'executer les instructions suivantes */
const luck = await somethingThatTakesTime();
return luck;
}
Mon explication est très succincte, car je ne veux pas trop digresser. Cependant, je vous conseille cette vidéo de Grafikart pour en apprendre plus.
Version 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>
La partie HTML reste sensiblement la même. Les anciens attributs du form
on laissé place à un id. On ajoute un paragraphe pour afficher le message de succès ou d’échec. Enfin, on charge notre script.
/* On récupère les éléments form et message */
const form = document.getElementById("form");
const message = document.getElementById("message");
/* Lors de la soumission du formulaire on previent
le comportement par défaut */
form.addEventListener("submit", async function (e) {
e.preventDefault();
/* L'astuce pour IE, si vous n'utilisez pas de polyfill, consiste
à inviter l'utilisateur à utiliser un autre navigateur */
if (!window.fetch || !window.FormData) {
alert(
"Tu crois que c'est du respect mon garçon ? Est ce que tu crois que c'est du respect d'utiliser un navigateur archaïque ?"
);
return;
}
/* Lorsque l'on instancie FormData on peut lui passer un élément
form en paramètre. De cette façon, FormData peut detecter chaque
input du formulaire et leur valeur.
Ici, this fait référence à form */
const formData = new FormData(this);
try {
/* fetch() prend en 1er paramètre l'url et en second paramètre
les options. Ici, nous indiquons que notre requête sera en POST
et que le corps de la requête sera constitué de nos formData. */
await fetch("http://localhost:4000/upload.php", {
method: "POST",
body: formData,
});
// On affiche un message suivant le résultat de la requête
message.innerText = "Fichier uploadé avec succès \\o/";
} catch (error) {
message.innerText =
"C'est la cata, c'est la cata, c'est la catastrophe /o\\";
}
// On réinitialise le formulaire
this.reset();
// On efface le message après deux secondes
setTimeout(() => {
message.innerText = "";
}, 2000);
});
C’est ainsi que l’on arrive à ce résultat.
La dernière étape est une optimisation du serveur. Les commentaires représentent les lignes devenue inutile et que nous pouvons donc supprimer.
<?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 "Problème serveur";
}
else {
http_response_code(201);
// readfile('success.html');
}
// header("Refresh:2; url=index.html", true, 303);
Conclusion
La combinaison des APIs FormData et fetch, rend l’upload de fichier très simple. On évite le rechargement de page ce qui améliore l’expérience utilisateur.
Pour effectuer des requêtes HTTP, vous pouvez très bien utiliser Fetch, comme présenté dans l’article mais vous devriez jeter un œil à :
- Axios, une librairie basée sur XMLHttpRequest, l’ancêtre de fetch. Elle est donc compatible avec IE.
- Ky, une librairie qui englobe fetch et propose de nouvelles fonctionnalités. Ky comparé à axios par le créateur de Ky.
Enfin, il est tout à fait possible d’utiliser ces principes avec des librairies comme React ou Vue.js. Il faudra simplement utiliser les références sur vos formulaires.
Merci de m’avoir lu.