Un tutoriel pour mettre en place un système d’upload de fichier avec une barre de progression pour suivre l’avancement de l’upload dans un projet Laravel - Vue.js
Une barre de progression peut être définie comme un composant qui indique l’état d’avancement d’une opération. Elle peut se présenter de la manière suivante pour une opération accomplie à 78 % :
Nous voulons voir dans ce guide comment mettre en place un formulaire d’upload de fichier avec une barre de progression pour suivre l’état d’avancement d’envoi d'un fichier vers le serveur en utilisant le framework JavaScript Vue.js dans un projet Laravel.
Vue.js va nous permettre d’envoyer le fichier vers le serveur via une requête Ajax (Asynchronous JavaScript + XML) avec Axios et suivre la progression de l’upload.
Pour mettre en place Vue.js dans Laravel, référez-vous au guide Installer Vue.js dans un projet Laravel. Vous pourrez ensuite vérifier la version de Vue.js et Axios en exécutant la commande npm ls vue axios
:
Nous allons monter notre système en commençant par le coté backend Laravel avec les routes et leurs actions puis nous reviendrons au frontend pour le composant Vue.js.
Pour faire fonctionner le système d’upload de fichier au niveau du client comme au niveau du serveur, nous avons besoin de :
Nous allons utiliser les routes suivantes pour le système d'upload :
(GET)
pour présenter la page du formulaire d’upload de fichier. Elle sera gérée par l’action « show » du contrôleur UploadFileController.php
(POST)
pour sauvegarder le fichier envoyé au niveau du serveur. Elle sera gérée par l’action « store » du contrôleur UploadFileController.php
Générons le contrôleur UploadFileController.php pour ces actions en exécutant la commande artisan suivante :
php artisan make:controller UploadFileController
Cette commande crée le fichier /app/Http/Controllers/UploadFileController.php que nous allons compléter au point suivant.
Nous pouvons maintenant définir les routes « upload » (GET
et POST
) au fichier /routes/web.php :
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UploadFileController;
// Afficher le formulaire
Route::get("upload", [ UploadFileController::class, "show" ]);
// Enregistrer le fichier uploadé
Route::post("upload", [ UploadFileController::class, "store" ]);
Le contrôleur /app/Http/Controllers/UploadFileController.php décrit les actions « show » et « store » de routes « upload » de la manière suivante :
Le code source du contrôleur /app/Http/Controllers/UploadFileController.php :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class UploadFileController extends Controller
{
// Affiche le formulaire d'upload
public function show () {
return view("show");
}
// Valide puis enregistre le fichier envoyé
public function store (Request $request) {
// La validation
$this->validate($request, [
"picture" => "bail|required|image|max:1024"
]);
// On enregistre l'image dans le répertoire "images"
$chemin = $request->picture->store("images");
// On retourne la réponse en JSON avec le chemin de l'image
return response()->json([
"chemin" => $chemin
]);
}
}
L'action « show » du contrôleur UploadFileController.php retourne la vue /resources/views/show.blade.php (template Blade). Cette vue peut se présenter de la manière suivante en y intégrant le composant Vue <upload-file>
que nous allons créer au point suivant :
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel - Vue.js - Upload + Progress bar</title>
</head>
<body>
<div id="app">
<h1>Laravel - Vue.js : Upload + Barre de progression</h1>
<!-- Le composant Vue d'upload de fichier -->
<upload-file></upload-file>
</div>
<!-- Le script app.js -->
<script type="text/javascript" src="{{ asset('js/app.js') }}" ></script>
</body>
</html>
Le composant Vue <upload-file>
que nous avons intégré sur la vue show.blade.php va contenir :
<template>
<script>
.Créons le fichier UploadFileComponent.vue pour ce composant dans le répertoire /resources/js/components/
Il faut ensuite enregistrer le composant au fichier /resources/js/app.js :
require('./bootstrap');
import Vue from "vue"
// Le composant d'upload de fichier
Vue.component(
"upload-file",
require("./components/UploadFileComponent.vue").default
)
// L'instance Vue
const app = new Vue({
el : "#app"
});
Puis exécuter la commande npm run dev
pour compiler les fichiers JavaScript dans /public/js/app.js.
Voici le code source complet (<template>
et <script>
) du composant /resources/js/components/UploadFileComponent.vue que nous allons commenter juste après :
<template>
<!-- Le formulaire d'upload avec la référence "upload_form" -->
<form v-on:submit.prevent="upload" enctype="multipart/form-data" ref="upload_form" >
<label for="picture" >Séléctionnez une image</label><br>
<input type="file" id="picture" name="picture" required accept=".jpg,.png,.gif" >
<!-- La barre de progression -->
<div>
<progress v-bind:value="pourcentage" max="100" >{{ pourcentage }} %</progress>
</span>{{ (pourcentage > 0) ? pourcentage + ' %' : '' }}
</div>
<button type="submit" >Uploader</button>
</form>
</template>
<script>
export default {
data () {
return {
// Pourcentage pour la barre de progression
pourcentage : 0
}
},
methods : {
// Upload l'image
upload () {
// 1. L'objet "formData" avec la référence du formulaire
let formData = new FormData(this.$refs.upload_form)
// 2. Si un fichier est sélectionné = il possède un nom
if (formData.get('picture').name) {
// 3. La configuration pour la requête Ajax avec axios
let config = {
/*
Lors de la progression de l'upload "onUploadProgress", on met à jour
le $pourcentage en divisant les bytes envoyées "progressEvent.loaded"
par les bytes attendues "progressEvent.total"
*/
onUploadProgress : progressEvent => {
this.pourcentage = Math.round((progressEvent.loaded * 100) / progressEvent.total)
}
}
// 4. On envoie la requête Ajax via axios avec les données du formulaire
axios.post('upload', formData, config)
.then((data) => {
// 5. Si l'image est uploadé
if (data.data.chemin) {
// On réinitialise le formulaire et le pourcentage
this.resetForm()
alert("Image uploadé")
}
})
.catch((error) => {
this.resetForm()
alert(error)
})
} else {
alert("Sélectionnez le fichier à uploader")
}
},
// Réinitialise le formulaire et le pourcentage
resetForm () {
this.$refs.upload_form.reset()
this.pourcentage = 0
}
}
}
</script>
La logique du composant Vue /resources/js/components/UploadFileComponent.vue peut se résumer en ces six points :
1. La donnée pourcentage
initialisé à « 0 » représente le niveau de progression de l'upload :
data () {
return {
pourcentage : 0
}
},
Sa valeur est liée (data binding) à l'attribut « value » de la barre de progression <progress>
:
<progress v-bind:value="pourcentage" max="100" >{{ pourcentage }} %</progress>
2. Lorsqu'on clique sur le bouton « Uploader » après avoir choisi un fichier, le formulaire envoyé est géré par la méthode upload()
:
<form v-on:submit.prevent="upload" enctype="multipart/form-data" ref="upload_form" >
3. Les données du formulaire sont récupérées dans la méthode upload()
via l'instance FormData à laquelle on transmet la référence du formulaire « upload_form » :
let formData = new FormData(this.$refs.upload_form)
4. On configure la méthode onUploadProgress(progressEvent)
de la requête Ajax d'axios pour suivre la progression de l'upload et mettre à jour le pourcentage :
let config = {
onUploadProgress : progressEvent => {
this.pourcentage = Math.round((progressEvent.loaded * 100) / progressEvent.total)
}
}
5. On envoie la requête XHR avec les données du formulaire et la configuration « onUploadProgress » :
axios.post('upload', formData, config).then((data) => {
// ...
}).catch((error) => {
// ...
})
6. Si la requête échoue ou réussit, on réinitialise le formulaire et le pourcentage en appelant la méthode resetForm()
:
resetForm () {
this.$refs.upload_form.reset()
this.pourcentage = 0
}
Portez-vous bien ! 😉
Cette publication vous a plu ?
Partagez-la avec vos ami(e)s sur les réseaux sociaux.
Wilo Ahadi, l'auteur
Passionné de l'informatique, je suis spécialiste en techniques des systèmes et réseaux, développeur web et mobile, infographiste et designer, ... J'aime partager mon expérience en formant sur la plateforme Akili School
Voir profilAutres publications
Voir toutes les publication de Wilo Ahadi
Sélection ebook
10 Laravel tips and techniques for your next PHP project
Développement web
Commentaires