jQuery : Uploader une image en AJAX avec un aperçu avant envoi

Le but de cet article est de :

  1. montrer comment afficher à l’utilisateur des informations sur le fichier sélectionné avec un input[type="file"], ce qui est très pratique pour éviter les erreurs. Et ceci sans plugin additionnel.
  2. uploader le fichier en AJAX à l’aide de jQuery.

La partie serveur, c’est-à-dire la réception du fichier et son stockage ne sera pas traitée dans cet article.

Le code HTML qui sera présenté sera le strict minimum fonctionnel, pour des questions de présentation, ma page de démonstration est plus élaborée. Voir la démonstration

Formulaire utilisé pour la démo de cet article

Formulaire utilisé pour la démo de cet article

Pour débuter, petit rappel sur l’envoi d’un formulaire (sans upload) en AJAX avec jQuery.

Envoi d’un formulaire avec jQuery

<form id="my_form" method="post" action="process_form.php">
    <input type="text" name="title">
    <textarea name="content"></textarea>
    <button type="submit">OK</button>
</form>
$(function () {
    $('#my_form').on('submit', function (e) {
        // On empêche le navigateur de soumettre le formulaire
        e.preventDefault();

        var $form = $(this);

        $.ajax({
            url: $form.attr('action'),
            type: $form.attr('method'),
            data: $form.serialize(),
            success: function (response) {
                // La réponse du serveur
            }
        });
    });
});
Inspection de la requête envoyée au serveur via Chrome Dev Tools

Inspection de la requête envoyée au serveur via Chrome Dev Tools

Ni plus ni moins pour un envoi de formulaire en AJAX. Pour davantage de détails et si vous n’êtes pas familier avec ce genre de code, j’ai fait un autre article « Envoyer un formulaire en AJAX avec jQuery et JSON« .

Ajouter un upload d’image

Ici mon exemple va se porter sur un upload d’image, mais peut s’adapter à n’importe quel autre type de fichier.

Voici le code qui supporte l’upload d’une image, en plus du reste du formulaire.

<form id="my_form" method="post" action="process_form.php" enctype="multipart/form-data">
    <input type="text" name="title">
    <textarea name="content"></textarea>
    <input type="file" name="image" accept="image/*">
    <button type="submit">OK</button>
</form>

On ajoute enctype="multipart/form-data" au formulaire, c’est obligatoire si on veut faire un upload de fichier. On ajoute également notre champ qui permet à l’utilisateur de sélectionner un fichier sur sa machine. On précise que seules les images sont acceptées.

$(function () {
    $('#my_form').on('submit', function (e) {
        // On empêche le navigateur de soumettre le formulaire
        e.preventDefault();

        var $form = $(this);
        var formdata = (window.FormData) ? new FormData($form[0]) : null;
        var data = (formdata !== null) ? formdata : $form.serialize();

        $.ajax({
            url: $form.attr('action'),
            type: $form.attr('method'),
            contentType: false, // obligatoire pour de l'upload
            processData: false, // obligatoire pour de l'upload
            dataType: 'json', // selon le retour attendu
            data: data,
            success: function (response) {
                // La réponse du serveur
            }
        });
    });
});

L’upload en AJAX est lié en fait au support du navigateur de l’API FormData. C’est supporté par les principaux navigateurs, du côté d’IE, c’est à partir de la version 10.

Mais le support de FormData ne suffit pas. Il faut aussi paramétrer la requête d’envoi :

  • contentType, quand non spécifié, la valeur prend 'application/x-www-form-urlencoded; charset=UTF-8' ce qui fonctionne pour un formulaire sans upload. Dans notre cas, il faut mettre la valeur à false.
  • processData, quand non spécifié, la valeur prend true. jQuery fait un pré-traitement avant envoi, il transforme les données en objet (voir le premier screenshot). Dans notre cas, il faut mettre la valeur à false.

Plus d’informations sur les paramètres de $.ajax().

Avec toute cette configuration, les données envoyées sont représentées comme ceci :

Inspection de la requête envoyée au serveur via Chrome Dev Tools

Inspection de la requête envoyée au serveur via Chrome Dev Tools

Ajouter un aperçu de l’image au moment de sa sélection

Pour ajouter un aperçu au moment de la sélection, un événement change sur le champ suffit. Tout le code à l’intérieur pour récupérer les données côté client est du Javascript natif. jQuery ne sert qu’à manipuler le DOM afin de montrer à l’utilisateur l’image choisie. Un console.log(files) vous donnera tout ce dont vous avez besoin.

$(function () {
    // A chaque sélection de fichier
    $('#my_form').find('input[name="image"]').on('change', function (e) {
        var files = $(this)[0].files;

        if (files.length > 0) {
            // On part du principe qu'il n'y qu'un seul fichier
            // étant donné que l'on a pas renseigné l'attribut "multiple"
            var file = files[0],
                $image_preview = $('#image_preview');

            // Ici on injecte les informations recoltées sur le fichier pour l'utilisateur
            $image_preview.find('.thumbnail').removeClass('hidden');
            $image_preview.find('img').attr('src', window.URL.createObjectURL(file));
            $image_preview.find('h4').html(file.name);
            $image_preview.find('.caption p:first').html(file.size +' bytes');
        }
    });

    // Bouton "Annuler" pour vider le champ d'upload
    $('#image_preview').find('button[type="button"]').on('click', function (e) {
        e.preventDefault();

        $('#my_form').find('input[name="image"]').val('');
        $('#image_preview').find('.thumbnail').addClass('hidden');
    });
});

Voir le résultat

En PHP, dans process_form.php, j’ai mis :

<?php
    $data['file'] = $_FILES;
    $data['text'] = $_POST;

    echo json_encode($data);
?>

Je vous invite à inspecter le code de la démo.

Vous aimerez aussi...