Depuis quelques temps, il existe au sein de nos chers navigateurs, l’API FileReader, qui permet, comme le résume bien cet article de MDN :
- de gérer les fichiers sélectionnés via les champs de formulaire « fichier »
- de supporter le glisser/déposer de fichiers dans le navigateur
Les extraits de code qui suivent sont fortement inspirés de l’article de Maxime Chaillou sur le drag&drop.
On crée tout d’abord une zone qui servira de réceptable à nos fichiers :
[html]<div id="dropfile">Drop your file here to deduplicate SMS</div>[/html]
On gère ensuite les évènements sur cet élement :
[javascript]
//Quand la souris, maintenue enfoncée, entre dans la zone, on la colore, pour indiquer l’interaction
$(document).on(‘dragenter’, ‘#dropfile’, function() {
$(this).css(‘border’, ‘3px dashed red’);
return false;
});
//Une fois que la souris est rentrée dans la zone, on actualise régulièrement l’état de la zone
$(document).on(‘dragover’, ‘#dropfile’, function(e){
e.preventDefault();
e.stopPropagation();
$(this).css(‘border’, ‘3px dashed red’);
return false;
});
//Si la souris, toujours enfoncée, ressort, on indique la fin de l’interaction
$(document).on(‘dragleave’, ‘#dropfile’, function(e) {
e.preventDefault();
e.stopPropagation();
$(this).css(‘border’, ‘3px dashed #BBBBBB’);
return false;
});
[/javascript]
Il faut ensuite gérer l’upload à proprement parler :
[javascript]
$(document).on(‘drop’, ‘#dropfile’, function(e) {
//Si le transfert est possible
if(e.originalEvent.dataTransfer){
//S’il y au moins un fichier
if(e.originalEvent.dataTransfer.files.length) {
//On bloque l’ouverture du fichier dans le navigateur, ce qui est l’action par défaut
e.preventDefault();
//On bloque ensuite la propagation de l’évènement "dépot de fichiers" pour éviter de déclencher toute action sur les éléments parents
e.stopPropagation();
$(this).css(‘border’, ‘3px dashed green’);
//On lance l’upload à proprement parler
upload(e.originalEvent.dataTransfer.files);
}
}
else {
$(this).css(‘border’, ‘3px dashed #BBBBBB’);
}
return false;
});
[/javascript]
La function upload() alors utilisée va nous permettre d’envoyer le contenu du fichier déposé à une autre fonction (par exemple pour l’envoyer à un serveur Web) :
[javascript]
function upload(files) {
var f = files[0] ;
var reader = new FileReader();
//La fonction handleReaderLoad est ici associée à l’évènement "onload", pour qu’elle se déclenche une fois le chargement du fichier terminé
reader.onload = handleReaderLoad;
//Pour les besoins de cette démonstration, on utilise ici readAsText() qui va lire le fichier sous forme de chaîne de caractères
//Mais on utilise plutôt readAsBinary, pour l’envoi à un serveur Web
reader.readAsText(f);
}
[/javascript]
La dernière fonction va quant à elle gérer le fichier à proprement parler (l’envoyer via Ajax, ou, comme ici, traiter la chaîne de caractères comme un XML et en dédoublonner le contenu) :
[javascript]
function handleReaderLoad(evt) {
var xml = evt.target.result
deduplicateSMS($.parseXML(xml));
}
[/javascript]
Pour cet exemple (qui vise à supprimer les SMS en double dans une sauvegarde XML), on utilise également le plugin jQuery FileSaver.js, qui permet de lancer le téléchargement de fichiers dans un navigateur depuis Javascript :
[javascript]
function deduplicateSMS(xmlSource) {
var ids = new Array();
var duplicates= 0;
var total= 0;
//On explore le XML et on vérifie si la clé de chaque noeud existe déja dans le tableau "ids"
$(xmlSource).find(‘allsms sms’).each(function(index,e) {
if (ids.indexOf($(this).attr(‘address’)+$(this).attr(‘date’))==-1) {
total+=1;
ids.push($(this).attr(‘address’)+$(this).attr(‘date’));
} else {
//Si c’est le cas, on le supprime
duplicates+=1;
$(this).remove();
}
});
$(xmlSource).find(‘allsms’).attr(‘count’,total);
//On convertit ensuite ce XML en chaîne de caractères via XMLToString, puis en "Blob" gérable par FileSaver.js
var blob = new Blob([XMLToString(xmlSource)], {type: "text/xml;charset=utf-8"});
//On lance ensuite le téléchargement
saveAs(blob, "sms.xml");
}
[/javascript]
La méthode XMLToString utilisée ici est très simple et permet juste de convertir un arbre XML en chaîne :
[javascript]
/* Source : http://www.dotnet-tricks.com/Tutorial/javascript/Y7Q9130612-Convert-string-to-xml-and-xml-to-string-using-javascript.html*/
function XMLToString(oXML)
{
//code for IE
if (window.ActiveXObject) {
var oString = oXML.xml; return oString;
}
// code for Chrome, Safari, Firefox, Opera, etc.
else {
return (new XMLSerializer()).serializeToString(oXML);
}
}
[/javascript]
Vous pouvez retrouver cet exemple sur Github : https://github.com/samy-r/sms-super-backup-deduplication
Une démo est disponible sur mon espace de test : http://lahaut.info/demos/sms-super-backup-deduplication/