Image de l'article Envoyer un fichier en ligne, partie 3

Envoyer un fichier en ligne, partie 3

Envoi d'un fichier en HTML 5 avec XMLHttpRequest

Je vais vous montrer comment envoyer un fichier avec une zone de glissé-déposé (drag and drop en anglais) et une barre de progression sans utiliser jQuery.

Nous allons utiliser des Events et leurs propriétés DataTransfer.

Nous allons créer un objet XMLHttpRequest, une fonction pour vérifier le poids et le type du fichier. C'est ici qu'HTML5 entre en jeux avec l'api File, puisqu'il est possible d'obtenir des informations sur des fichiers stockés localement (sur la machine du visiteur).

HTML


<form id="uploadForm" action="upload.php" method="POST" enctype="multipart/form-data">
	<input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="10485760" />
	<p id="uploadFileSelect"><label for="img">Image : </label><input id="uploadFileButton" name="img" type="file"></p>
	<p id="uploadFileDropArea">Glissez votre image ici</p>
	<p><label> </label><span class="txtGris">Fichier de type JPEG, PNG, GIF</span></p>
	<p><label> </label><span class="txtGris">Poids maximum : 10 Mo</span></p>
	<p id="uploadSend"><label> </label><input type="submit" value="Envoyer"></p>
</form><!--uploadForm-->

<div id="uploadContent">
	<h1>Image</h1>
	<div id="uploadProgress" class="progressBar"><span id="uploadProgressBar" style="width: 0%"></span></div>
	<p id="uploadResult"></p>
</div><!--uploadContent-->

Nous avons deux blocs <p> permettant de sélectionner un fichier :

Si le visiteur utilise un navigateur compatible avec l'envoi de fichier nous affichons la zone de glissé-déposé (#uploadFileDropArea), sinon nous affichons le champ de sélection (#uploadFileSelect).

Comment savoir si le navigateur est compatible avec l'envoi de fichier ?

Il suffit de créer un objet XMLHttpRequest et de vérifier que la méthode upload soit disponible.


<script type="text/javascript">
//<![CDATA[
var objRequest = new XMLHttpRequest();
if(objRequest.upload){
	// le navigateur est compatbible
	
}else{
	// le navigateur n'est pas compatbible
}
//]]>
</script>

Javascript

Maintenant que nous savons déterminer si le navigateur peut envoyer des fichiers avec XMLHttpRequest, commençons par initialiser quelques variables de bases.


// zone de glissé déposé
var dropArea = document.getElementById("uploadFileDropArea");
// bloc div dans lequel on va affiche le résultat de l'envoi du fichier
var uploadResult =  document.getElementById("uploadResult");
// bloc p qui contient le champ de sélection pour les navigateurs non compatibles
var uploadFileSelect =  document.getElementById("uploadFileSelect");
// objet XMLHttpRequest
var objRequest = new XMLHttpRequest();

Si le navigateur est compatible, on initialise et affiche la zone de glissé déposé, puis on masque le bouton <input type="file" />


if(objRequest.upload){
	// un fichier entre dans la zone
	dropArea.addEventListener("dragover", FileDrag, false);
	// un fichier sort dans la zone
	dropArea.addEventListener("dragleave", FileDrag, false);
	// un fichier est déposé dans la zone
	dropArea.addEventListener("drop", FileSelectHandler, false);
	// affiche la zone sur laquelle l'utilisateur peut glisser un fichier
	dropArea.style.display = "block";
	// masque le bouton de sélection de fichier
	uploadFileSelect.style.display = "none";
}

Nous venons d'attacher deux fonctions à la zone de glissé déposé : FileDrag() et FileSelectHandler(). Les deux fonctions reçoivent un paramètre de type event.


function FileDrag(evt){
	// stop la diffusion de l'évenements dans le DOM
	evt.stopPropagation();
	// empêche le navigateur d'exécuter son comportement par défaut (ouvrir ou afficher le fichier glissé)
	evt.preventDefault();
	// change la classe du bloc
	dropArea.className = (evt.type=="dragover" ? "hover":"");
}

function FileSelectHandler(evt){
	// appele la fonction qui gère le drag and drop pour stopper l'évênement
	FileDrag(evt);
	// charge le fichier
	UploadFile(evt.dataTransfer.files[0]);
}

Par défaut, lorsque vous glissez un fichier sur une fenêtre de navigateur, il va chercher à l'afficher ou à l'ouvrir. Pour empêcher ce comportement on utilise la fonction preventDefault().

Le navigateur va également propager l'événement à travers la chaine DOM. Même si dans cet exemple ça ne pose pas de problème, il est important de prendre l'habitude de stopper cette propagation avec la fonction... stopPropagation().

FileSelectHandler() va appeler FileDrag() pour stopper la diffusion de l'événement et chargera ensuite le fichier glissé. UploadFile() est appelé comme unique paramètre le fichier à transférer.

Pour accéder au fichier, nous allons utiliser la propriété dataTransfer de l'objet event. Datatransfer n'est disponible que lors des opérations de glissé déposé :

Datatransfer possède l'attribut files qui est une liste de fichier. Dans cet exemple on ne cherche pas à envoyer plusieurs fichiers, il suffit donc d'accéder au premier fichier de la liste. Comme c'est une liste Javascript, le premier élément est indexé à 0 : evt.dataTransfer.files[0]


UploadFile(evt.dataTransfer.files[0]);

Gérer l'envoi du fichier en Javascript

Nous avons accès au fichier grâce à un objet event, il ne reste plus qu'à l'envoyer !


function UploadFile(file){
	if(!objRequest.upload){
		uploadResult.innerHTML = "Votre naviguateur ne supporte pas l'envoi de fichier";

Si la méthode upload n'est pas disponible, on ne peut pas envoyer de fichier avec XMLHttpRequest.


	}else if(file.size > document.getElementById("MAX_FILE_SIZE").value){
		// le fichier est trop gros
		uploadResult.innerHTML = "Le fichier est trop gros. Limite : "+FileConvertSize(document.getElementById("MAX_FILE_SIZE").value);

	}else if(!(file.type=="image/jpeg" || file.type=="image/png" || file.type=="image/gif")){
		// le fichier n'est pas du bon type
		uploadResult.innerHTML = "Le type du fichier est incorrect : PNG, JPG, GIF";

Avant d'envoyer le fichier on vérifie son poids et son type mime.

Pour le poids c'est très simple. Nous avions défini MAX_FILE_SIZE dans le formulaire. Il suffit de comparer ce champ avec le poids du fichier, que l'on trouve grâce à file.size. Les deux étant exprimé en octets nous n'avons aucune conversion à faire !

Pour le type MIME, il suffit de comparer file.type avec les différents types de fichier que nous souhaitons traiter. Ici du JPEG, du PNG et du GIF. J'ai écrit un article sur les types MIME courant, n'hésitez pas à le consulter.


	}else{
		// tout est ok, on peut envoyer le fichier
		// crée une fonction pour afficher la progresion de la requete
		objRequest.upload.onprogress = function(evt){
			if(evt.lengthComputable){
				var percent = Math.ceil((evt.loaded / evt.total)*100);
				document.getElementById("uploadProgressBar").style.width = percent+'%';
				uploadResult.innerHTML = percent+' %';
			}
		}
		// onreadystatechange est appelé à chaque changement d'état de la requete
		objRequest.onreadystatechange=function(){
			if(objRequest.readyState==4 && objRequest.status==200){
				uploadResult.innerHTML = objRequest.responseText;
			}
		}

Le fichier n'est pas trop gros et il est au bon format, on peut enfin l'envoyer! Nous allons préparer deux fonctions que nous allons attacher à notre objet XMLHttpRequest.

La première nous servira à afficher la progression du chargement avec onprogress. La function onprogress passe un paramètre avec 3 attributs en lecture seule :

Si lengthComputable est égal à 0, le problème vient sans doute de votre serveur qui n'envoie pas une valeur Content-Length correcte. Si la valeur est correcte, il suffit de faire une division pour connaître le pourcentage chargé.

La seconde fonction est appelé à chaque changement d'état de notre objet XMLHttpRequest. On l'attache à onreadystatechange et on vérifie la valeur de l'attribut readyState :

Pendant l'envoi du fichier onreadystatechange sera donc appelé 5 fois.

La valeur qui nous intéresse est readyState==4, ce qui veut dire que la requête est terminée, mais cela ne suffit pas. Il faut également vérifier que le serveur a renvoyé un statut 200 OK. Si c'est le cas, tout va bien sinon il faudra afficher un message d'erreur à l'utilisateur !

Ne reste plus qu'à afficher la réponse du serveur avec objRequest.responseText. Si le serveur a renvoyé des données XML vous pouvez les traiter avec objRequest.responseXML.

XMLHttpRequest.open() et XMLHttpRequest.send()

La fin approche !

Le fichier est prêt à être envoyé et nous avons les fonctions pour traiter les réponses du serveur. Mais il manque l'essentiel, c'est-à-dire envoyer le fichier. Heureusement c'est très simple, quelques lignes suffisent.


		// créer l'objet FormData
		var formData = new FormData();
		// ajoute l'image
		formData.append("img", file);
		// ouvre une requete post, avec l'adresse du formulaire
		objRequest.open("POST", document.getElementById("uploadForm").action);
		// on envois le fichier
		objRequest.send(formData);
		// indique le début du chargement
		uploadResult.innerHTML = "Chargement en cours";
		// affiche la barre de progression
		document.getElementById("uploadProgress").style.display = "block";
		// masque le bouton d'envoi
		document.getElementById("uploadSend").style.display = "none";
	}
}

1- Nous allons créer un objet de type FormData. Un objet FormData c'est une suite d'entrée avec pour chacune un nom et une valeur. Comme une liste indexée en PHP.

2- On ajoute l'image. On accédera à l'image avec la superglobale $_FILES['img']

3- Initialise la requête avec open. Open peut recevoir 5 paramètres

4- Envoi la requête avec send. Send ne reçoit aucun paramètre.

5- On affiche la barre de progression

PHP

Le fichier PHP est le même que dans l'article sur la méthode d'envoi de fichier avec jQuery.

Exemple concret

Voir cet exemple en fonctionnement.

Pour une utilisation réelle il faut prévoir deux fichiers PHP différents :

Dans cette démo j'ai été un peu flemmard.

Liens

Article précédent : Convertir des octets en Javascript

Article suivant : Vous connaissez Adobe ExtendScript Toolkit ?

 

Image Viewer