upload avec barre de progression

dans cet article-tuto nous allons voir comment mettre en place un système d’upload avec une sympathique barre de progression.

les pré-requis :
– un serveur web Apache
– PHP5 avec l’extension APC
jquery

dans ce tuto je pars du principe qu’Apache et PHP sont installés et fonctionnels, on va juste voir comment installer APC et activer le suivi de la progression d’upload :

apt-get install php-apc
vim /etc/php5/apache2/conf.d/apc.ini

ajouter à la fin du fichier :

apc.rfc1867 = on

cette ligne magique, activera la suivi de progression d’upload sur le serveur.

on reload apache :

/etc/init.d/apache2 reload

le coté système de la chose est maintenant terminé, passons à l’upload :

pour la petite histoire, voici ce que l’on va faire :
on va créer un formulaire, qui sera validé vers (target) une iframe invisible, le temps que l’upload se passe, on va contacter un petit script php en ajax (très souvent) qui va nous dire où en est l’upload, en nous retournant le pourcentage d’avancement (tout simplement), avec cette valeur, on va pouvoir mettre en place une barre de progression très facilement.

pour commencer, le formulaire :

<?php $unique_id = uniqid(); //id unique de notre formulaire ?>
 
<!-- iframe caché en javascript dans laquelle se fait l'upload, elle affichera le message de confirmation / ou erreur à la fin de l'upload -->
<iframe frameborder="0" border="0" id="upload_to" name="upload_to" style="width: 100%; border:none; border-color: #fff;" scrolling="no"></iframe>
 
<!-- le formulaire d'upload -->
<form id="upload_form" enctype="multipart/form-data" action="upload.php" method="post" target="upload_to">
<input type="hidden" name="APC_UPLOAD_PROGRESS" id="progress_key" value="<?php echo $unique_id?>"/>
<div id="progressouter">
	<div id="progressinner"><span id="progression_percent">0&nbsp;%</span></div>
</div>
<p><input id="file" type="file" name="file" size="30" /></p>
<p><input type="submit" value="valider" /></p>
</form>

le bout de php au début, permet de donner un id unique au formulaire
il est renseigné dans le champ « APC_UPLOAD_PROGRESS » ceci permet au système de donner un id à l’upload en cours, ce qui nous permettra de consulter la progression en le connaissant (l’id).

l’iframe n’a rien de spéciale, elle est caché en javascript (voir plus bas).

le formulaire contient l’HTML de la barre de progression (la div progressouter, on pourrait la mettre ailleurs, chacun fait ce qu’il veut. et le champ hidden APC_UPLOAD_PROGRESS comme dit plus haut, obligatoire pour le suivi de l’upload.

passons maintenant à notre script d’upload : upload.php
l’upload en lui même n’est pas vraiment notre sujet du jour, donc, je prends l’exemple d’upload le plus simple au monde (donné sur php.net)

$uploaddir = '/var/www/uploads/';
$uploadfile = $uploaddir . basename($_FILES['file']['name']);
 
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
    echo "File is valid, and was successfully uploaded.\n";
} else {
    echo "Possible file upload attack!\n";
}

on aura besoin du fameux script qui retourne le pourcentage d’avancement de l’upload : progress.php

header("Cache-Control: no-cache"); //utile pour IE (problème avec l'ajax)
//progression
if(isset($_GET['progress_key'])) {
    $upload = apc_fetch('upload_'.$_GET['progress_key']);
    $percent = 1000;
    if ($upload) {
        if ($upload['done'])
            $percent = 100;
        elseif ($upload['total'] == 0)
            $percent = 0;
        else
            $percent = $upload['current'] / $upload['total'] * 100;
    }
 
    echo number_format($percent, 2);
}

c’est presque fini, il ne reste plus que le javascript :

function getProgress(){
    var count = Math.random(); /// Math.random sélectionne un nombre entre 0 et 1 ( ex: 0.6489534931546957) pour IE aussi (même si le header no-cache devrait suffire)
    $.ajax({
        type: "GET",
        url: "/progress.php?progress_key=<?php echo($unique_id)?>&count="+count,
        success: function(percent) {
            if (percent <= 100){
                $("#progressinner").css("width", percent+"%");
                $("#progression_percent").text(percent+" %");
            }
            if (percent < 100){
                setTimeout("getProgress()", 100);
            }
            if (percent == 100){
                $("#upload_to").show("slow");
                $("#file").val('');
            }
        }
    });
}
 
function startProgress(){
    $("#progressouter, #progression_percent").show(200);
    setTimeout("getProgress()", 1000);
}
 
 
$(document).ready(function(){
    $("#upload_to").hide();
    $('#upload_form').submit(function () {
        startProgress();
    });
});

en gros (très gros même) ce js va cacher l’iframe au chargement de la page, et à la validation du formulaire, il va lancer la fonction startProgress(), qui elle, va afficher la barre de progression et lancer régulièrement getProgress, qui elle, fait un appel ajax à notre script progress.php et lui indiquant l’unique id généré tout au début de la page et en fonction du retour, elle va ajuster la largeur de la barre de progression.

si vous avez des questions, n’hésitez pas à les poser dans les commentaires.

Share

8 Comments

  1. Je suis entrain de me faire un site internet et j’aimerais bien avoir un upload avec barre de progression ,mais l’exemple que tu donnes ne fonctionne pas ,je suis avec windows xp

    Répondre

  2. l’exemple est fait sous debian, à priori ça devrait fonctionner pour windows aussi.

    t’as bien installé APC et changé le bon php.ini (la toute première partie du tuto) ? fais un phpinfo() quelque part pour t’assurer que tes paramètres sont bon et qu’apc est bien installée.

    Répondre

  3. Bonjour, je n’arrive pas non plus à faire fonctionner ton exemple.
    L’apc est bien installé, l’upload se fait bien mais pas la progression

    Répondre

    1. Bonjour,

      installer apc ne suffit pas parfois; il faut ajouter à la main
      apc.rfc1867 = on dans le fichier /etc/php5/apache2/conf.d/apc.ini (le créer si n’existe pas)

      normalement avec ça, tout rentre dans l’ordre.

      Répondre

  4. Bonjour, j’ai également un soucis avec ton exemple, la progression ne s’affiche pas non plus.
    Il faut dire que j’ai du mal à comprendre où doit être placé le javascript…
    Merci d’avance

    Répondre

    1. bonjour,

      le JS est à mettre en fin de page, juste avant la balise body fermant.

      en tout cas, son emplacement importe peu vu qu’il est lancé en asynchrone.

      Répondre

  5. Bonjour, j’ai installé ton code dans mon framework seul problème quand j’essaye d’uploader un fichier lourd exemple 200 mo le pourcentage ne bouge pas.
    Une solution ?

    Répondre

    1. j’ai aussi rencontré des problèmes avec de gros fichiers, je pense que cette solution n’est pas adapté pour les gros uploads, il faudrait trouver autre chose, je cherche, dès que j’ai une autre solution plus fiable, je fais un tuto :)

      Répondre

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *