octobre
2010
Cet article décrit comment j’ai mis en place l’upload de fichiers vers un serveur Django + une barre de progression avec Ajax et jQuery. J’avais besoin de cette fonctionnalité pour que les utilisateurs de Gourmious puissent poster leurs photos de plats.
Côté client
On a besoin d’un formulaire pour que l’utilisateur puisse uploader un fichier.
<input type="file" name="picture" id="picture" />
<input type="hidden" id="X-Progress-ID" name="X-Progress-ID" value=""/>
<input type="hidden" id="id" name="id" value=""/>
<input id="form_submit_button" class="tp-button" type="submit" value="Submit" />
</form>
Nous avons ajouté 2 inputs, la première ‘X-Progress-ID’ est l’identifiant de l’upload pour que l’on puisse supporter plusieurs uploads simultanément.
On a l’input ‘id’ qui représente l’identifiant du plat dans notre cas.
On va utiliser Ajax pour la requête POST pour pouvoir l’intégrer dans une interface web moderne avec une barre de progression. Pour cela, on va utiliser le plugin jQuery Form.
La méthode ajaxSubmit() va s’occuper de tout pour nous
On génère une string aléatoire pour l’ID de l’upload et on l’attribue à l’input ‘X-Progress-ID’.
On a besoin de spécifier l’URL pour l’upload et 2 functions: 1 qui est appelée avant l’upload et une après.
$('#X-Progress-ID').val('random string'); var options = { dataType: 'xml', url: '/upload?X-Progress-ID='+$('#X-Progress-ID').val(), beforeSubmit: showRequest, success: showResponse } $('#form_upload').ajaxSubmit(options);
Quand l’utilisateur clique sur le bouton pour démarrer l’upload, on doit présenter une barre de progression. Pour cela, on utilise le code JS suivant pour ajouter une barre de progression au formulaire. La méthode progressBar() fait partie de jQuery progress bar plugin.
$('#form_upload').find('#uploadprogressbar').progressBar();
On a aussi besoin d’une fonction pour récupérer la progression de l’upload à partir du serveur pour mettre à jour la barre de progression régulierement.
Pour faire cela, on utilise setInterval() afin d’émettre une requête GET pour récupérer la valeur de la progression au format JSON. On passe l’identifiant de l’upload dans la requête. L’upload est terminée quand la valeur null est renvoyée.
function startProgressBarUpdate(upload_id) { $("#uploadprogressbar").fadeIn(); if(g_progress_intv != 0) clearInterval(g_progress_intv); g_progress_intv = setInterval(function() { $.getJSON("/web_modules/process.py?request=get_upload_progress&X-Progress-ID=" + upload_id, function(data) { if (data == null) { $("#uploadprogressbar").progressBar(100); clearInterval(g_progress_intv); g_progress_intv = 0; return; } var percentage = Math.floor(100 * parseInt(data.uploaded) / parseInt(data.length)); $("#uploadprogressbar").progressBar(percentage); }); }, 5000); }
Coté serveur
On a besoin d’une méthode dans views.py pour recevoir les fichiers. On lit chaque fichier bloc par bloc pour éviter d’utiliser trop de RAM.
def upload(request): id = request.POST['id'] path = '/var/www/pictures/%s' % id f = request.FILES['picture'] destination = open(path, 'wb+') for chunk in f.chunks(): destination.write(chunk) destination.close()
Comment le serveur suit la progression de l’upload ? On doit utiliser un file handler spécial. On utilise UploadProgressCachedHandler. On a juste besoin de la classe, pas la méthode pour la vue car on va s’en occuper nous même. Vous pouvez ajouter cette classe dans un fichier s’appelant uploadprogresscachedhandler.py dans votre projet Django.
Pour activer cet handler, on ajoute les lignes suivantes dans settings.py.
from django.conf import global_settings FILE_UPLOAD_HANDLERS = (uploadprogresscachedhandler.UploadProgressCachedHandler', ) \ + global_settings.FILE_UPLOAD_HANDLERS
On a aussi besoin d’activer un système de cache. On va utiliser memcached. On ajoute la ligne suivante dans settings.py.
CACHE_BACKEND = 'memcached://127.0.0.1:11211/'
Assurez vous d’avoir memcached et les bindings python installés sur votre serveur.
On a besoin d’ajouter une méthode dans views.py pour retourner la progression de l’upload au client quand il le demande. La valeur de la progression se trouve dans la cache spécifiée par la clé remoteaddress_uploadid
def get_upload_progress(request): from django.utils import simplejson cache_key = "%s_%s" % (request.META['REMOTE_ADDR'], request.GET['X-Progress-ID']) data = cache.get(cache_key) return HttpResponse(simplejson.dumps(data))
Voila, c’est tout pour l’instant. N’hesitez pas à ajouter des commentaires pour continuer la discussion.
Si vous avez aimé cet article, ça serait cool d’aller sur mon site Gourmious pour découvrir et partager vos plats préférés au resto.