Monter des fichiers vers un serveur au travers d'une page web peut s'avérer ardu. En effet, plusieurs choix de technologies s'offrent au développeur.
On peut utiliser un <input type="file">
dans un formulaire html -
Ça fonctionne bien, mais si le fichier est un peu gros, ou la connexion mauvaise, l'opération peu prendre
un certains temps et l'utilisateur n'a pas de retour visuel de ce qui est en train de se passer.
On peut aussi utiliser HTML5 et sa "file api", qui comporte de certains avantages comme celui de controler la taille des fichiers avant l'upload, voir de rééchantillonner les images ... Mais ça ne fonctionne pas avec tout les navigateurs, il faut donc penser à une solution de repli (Fallback).
php, depuis la version 5.4, possède une option session.upload_progress qui, si elle est activée permet de traquer la progression de la montée d'un fichier vers le serveur,
d'ou le montage suivant:
Une page html, qui contient une balise iframe (un iframe ou une iframe ? La balise iframe, c'est bien)
et une barre de progression.
Dans l'iframe, un formulaire qui contient l'<input type="file">
.
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="myform">
$key = ini_get("session.upload_progress.prefix") . $_POST[ini_get("session.upload_progress.name")];
$_SESSION[$key]
Lorsque l'on soumet le formulaire, un évènement javascript appelle une fonction de la page html parent.
Cette fonction va aller demander au serveur l'état de la progression de l'upload du fichier toutes les x
milisecondes (via un setInterval()
) et mettre à jour la barre de progression.
Lorsque l'upload se termine, c'est à dire lorsque l'iframe se recharge et affiche son contenu, une fonction la page html parent est appelée pour mettre fin au timer javascript.
<legend>Envoyer un fichier vers le serveur: <span class="label label-warning">Limité à 2Mo</span> </legend> <div id="contframe_upd"> <iframe name="frame_upd" id="frame_upd" src="iframe_url" style="width:400px; height:80px; overflow: hidden;" frameborder="0" scrolling="no"></iframe> <div class="progress progress-striped"> <div class="bar" id="inupload_progress"></div> </div> </div>
L'iframe est une page html complète, avec un doctype, et des tags html, body ....
que j'ai enlevés pour réduire la longueur du code.
C'est aussi cette page qui se recharge après l'upload, un message de succès ou d'erreur
après l'upload peut être afficher.
<div id="conformfile"> <div id="inconformfile"> <form id="formfile" method="post" action="uploadfile_url" enctype="multipart/form-data"> <input type="hidden" data-updid="myForm" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="myForm"> <input type="file" name="photo" id="photo"/> <input type="submit" value="Envoyer"> </form> </div> </div>
var t_upload; var updid; var updidval; // function appelée par l'iframe à l'envoi du formulaire. function triggerupload() { t_upload = setInterval(function(){ var params = new Object(); params[updid] = updidval; $.post("/index.php/notes/getuploadprogress", params, function(data){ $("#inupload_progress").width(data.res + "%"); }, "json"); }, 500); } // function appelée par l'iframe après l'upload. function cleartimerupload(id, val, result){ updid = id; updidval = val; if( result == 1 ){ $("#inupload_progress").width("100%"); } else { $("#inupload_progress").width(0); } clearInterval(t_upload); }
// !! variable result initialisée avec php en fonction du résultat de l'upload. <?php if ( isset($result) ){?> var result = 1; <?php } else { ?> var result = 0; <?php } ?> $(function(){ var updid = $("input[data-updid]").attr("name"); var updidval = $("input[data-updid]").val(); parent.window.cleartimerupload(updid, updidval, result); $("#formfile").on("submit", function(){ parent.window.triggerupload(); }); });
function getuploadprogress(){ session_start(); $key = ini_get("session.upload_progress.prefix") . $_POST[ini_get("session.upload_progress.name")]; if (!empty($_SESSION[$key])) { $current = $_SESSION[$key]["bytes_processed"]; $total = $_SESSION[$key]["content_length"]; $current < $total ? $res = ceil($current / $total * 100) : $res = 100; } else { $res = 100; } echo json_encode(array("res" => $res)); }
J'ai trouvé de l'aide sur l'utilisation de session.upload_progress ici: