Système de transfert multiple de fichiers à l'aide de drag and drop

Andrey Prikaznov

Traducteur

Sébastien Germez

Dans cet article nous allons voir comment créer une interface de transfert de fichiers multiple en HTML5 basée sur le drag & drop (glisser déposer).

Commentez

- Titre : Système de transfert multiple de fichiers à l'aide de drag and drop

- Auteur : Andrey Prikaznov

- Traducteur : Sébastien Germez

- Parution : 20 mars 2013

- Mise à jour : 20 mars 2013

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2013 Andrey Prizkanov. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Traduction

- II. Introduction

- III. Étape 1 : le code HTML

- IV. Étape 2 : le code CSS

- V. Étape 3 : le code JavaScript et HTML5

- VI. Étape 4 : le code PHP

- VII. Conclusion

- VIII. Remerciements

I - Traduction

Cet article est la traduction la plus fidèle possible de l'article de Andrey Prikaznov, HTML5 Drag and Drop Multiple File Uploader.

Vous pouvez voir un exemple en ligne ou télécharger l'archive de l'exemple.

II - Introduction

Ce nouvel article va vous parler du transfert de fichiers en HTML5. Oui, nous avons déjà parlé d'upload en HTML5 dans des articles précédents, mais aujourd'hui, nous allons nous pencher sur un nouvel exemple, le plus intéressant.

L'exemple de cet article va vous permettre de sélectionner les images à télécharger en utilisant le drag & drop (plusieurs images en même temps). De plus, le script va nous permettre d'afficher la progression générale du transfert (en pourcentage et avec le nombre de fichiers restants) et la réponse du serveur (sans l'upload actuel). Nous allons afficher la progression en temps réel dans un canvas. Pour finir, le script permet de télécharger des fichiers non seulement sur notre propre serveur mais sur un autre également (on pourra appeler ça un uploader cross-sites).

III - Étape 1 : le code HTML

index.html

<div class="container">
<div class="contr"><h2>Glissez - déposez vos fichiers dans la « zone de drop » (maximum cinq fichiers - taille maximale par fichier 256 kb)</h2></div>
<div class="upload_form_cont">
<div id="dropArea">Zone de drop</div>
<div class="info">
<div>Fichiers restants : <span id="count">0</span></div>
<div>URL de destination : <input id="url" value="http://www.script-tutorials.com/demos/257/upload.php"/></div>
<h2>Résultat :</h2>
<div id="result"></div>
<canvas width="500" height="20"></canvas>
</div>
</div>
</div>
<script src="js/script.js"></script>

Comme vous pouvez le voir, le code se compose de plusieurs éléments principaux : la zone pour le drag and drop à gauche et la zone d'information à droite. Quand on déplace une image dans la zone de gauche, la réponse du serveur s'affiche dans la zone d'information de droite. Faites bien attention à ce que l'URL de destination soit bien renseignée. Dans l'exemple, c'est notre serveur que nous utilisons mais vous pouvez le changer à tout moment.

IV - Étape 2 : le code CSS

Nous pouvons maintenant personnaliser notre affichage.

css/main.css

.container {
overflow:hidden;
width:960px;
margin:20px auto;
}
.contr {
background-color: #212121;
color: #FFFFFF;
padding: 10px 0;
text-align: center;

border-radius:10px 10px 0 0;
-moz-border-radius:10px 10px 0 0;
-webkit-border-radius:10px 10px 0 0;
}
.upload_form_cont {
background: -moz-linear-gradient(#ffffff, #f2f2f2);
background: -ms-linear-gradient(#ffffff, #f2f2f2);
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f2f2f2));
background: -webkit-linear-gradient(#ffffff, #f2f2f2);
background: -o-linear-gradient(#ffffff, #f2f2f2);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2');
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f2f2f2')";
background: linear-gradient(#ffffff, #f2f2f2);

color: #000;
overflow: hidden;
}
.info {
background-color: #EEEEEE;
border: 1px solid #DDDDDD;
float: left;
font-weight: bold;
height: 530px;
margin: 20px;
position: relative;
width: 560px;
}
.info > div {
font-size: 14px;
font-weight: bold;
padding: 10px 15px 5px;
}
.info > h2 {
padding: 0 15px;
}
.info > canvas {
margin-left: 15px;
margin-bottom: 10px;
}
.info #url {
width: 400px;
}
#dropArea {
background-color: #DDDDDD;
border: 3px dashed #000000;
float: left;
font-size: 48px;
font-weight: bold;
height: 530px;
line-height: 530px;
margin: 20px;
position: relative;
text-align: center;
width: 300px;
}
#dropArea.hover {
background-color: #CCCCCC;
}
#dropArea.uploading {
background: #EEEEEE url(loading.gif) center 30% no-repeat;
}
#result .s, #result .f {
font-size: 12px;
margin-bottom: 10px;
padding: 10px;

border-radius:10px;
-moz-border-radius:10px;
-webkit-border-radius:10px;
}
#result .s {
background-color: #77fc9f;
}
#result .f {
background-color: #fcc577;
}

V - Étape 3 : le code JavaScript et HTML5

js/script.js

// variables
var dropArea = document.getElementById('dropArea');
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var count = document.getElementById('count');
var destinationUrl = document.getElementById('url');
var result = document.getElementById('result');
var list = [];
var totalSize = 0;
var totalProgress = 0;

// initialisation
(function(){

// gestionnaires
function initHandlers() {
dropArea.addEventListener('drop', handleDrop, false);
dropArea.addEventListener('dragover', handleDragOver, false);
}

// affichage de la progression
function drawProgress(progress) {
context.clearRect(0, 0, canvas.width, canvas.height); // effacer le canvas

context.beginPath();
context.strokeStyle = '#4B9500';
context.fillStyle = '#4B9500';
context.fillRect(0, 0, progress * 500, 20);
context.closePath();

// affichage de la progression (mode texte)
context.font = '16px Verdana';
context.fillStyle = '#000';
context.fillText('Progression : ' + Math.floor(progress*100) + ' %', 50, 15);
}

// survol lors du déplacement
function handleDragOver(event) {
event.stopPropagation();
event.preventDefault();

dropArea.className = 'hover';
}

// glisser déposer
function handleDrop(event) {
event.stopPropagation();
event.preventDefault();

processFiles(event.dataTransfer.files);
}

// traitement du lot de fichiers
function processFiles(filelist) {
if (!filelist || !filelist.length || list.length) return;

totalSize = 0;
totalProgress = 0;
result.textContent = '';

for (var i = 0; i < filelist.length && i < 5; i++) {
list.push(filelist[i]);
totalSize += filelist[i].size;
}
uploadNext();
}

// à la fin, traiter le fichier suivant
function handleComplete(size) {
totalProgress += size;
drawProgress(totalProgress / totalSize);
uploadNext();
}

// mise à jour de la progression
function handleProgress(event) {
var progress = totalProgress + event.loaded;
drawProgress(progress / totalSize);
}

// transfert du fichier
function uploadFile(file, status) {

// création de l'objet XMLHttpRequest
var xhr = new XMLHttpRequest();
xhr.open('POST', destinationUrl.value);
xhr.onload = function() {
result.innerHTML += this.responseText;
handleComplete(file.size);
};
xhr.onerror = function() {
result.textContent = this.responseText;
handleComplete(file.size);
};
xhr.upload.onprogress = function(event) {
handleProgress(event);
}
xhr.upload.onloadstart = function(event) {
}

// création de l'objet FormData
var formData = new FormData();
formData.append('myfile', file);
xhr.send(formData);
}

// transfert du fichier suivant
function uploadNext() {
if (list.length) {
count.textContent = list.length - 1;
dropArea.className = 'uploading';

var nextFile = list.shift();
if (nextFile.size >= 262144) { // 256 kb
result.innerHTML += '<div class="f">Fichier trop gros (dépassement de la taille maximale)</div>';
handleComplete(nextFile.size);
} else {
uploadFile(nextFile, status);
}
} else {
dropArea.className = '';
}
}

initHandlers();
})();

La majorité du code est déjà commentée. J'espère que vous pouvez comprendre ce que fait ce code. Voici tout de même quelques explications sur ce que fait ce script JavaScript. Pour commencer, on lie deux gestionnaires d'événements à notre zone de drop : « drop » et « dragover ». Quand on déplace un fichier dans la zone de drop et qu'on ne lâche pas ce dernier, on peut appliquer un style personnalisé à notre zone. Ensuite, quand on lâche le fichier dans la zone, notre script lance la fonction « processFiles » qui met tous les fichiers dans un array et les transfère un par un. Dans le résultat, nous envoyons les données à travers un objet XMLHttpRequest au serveur. Pendant l'envoi des fichiers au serveur, nous affichons la progression totale dans notre canvas.

VI - Étape 4 : le code PHP

upload.php

<?php

// fixe le niveau de rapport d'erreur
if (version_compare(phpversion(), '5.3.0', '>=') == 1)
error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
error_reporting(E_ALL & ~E_NOTICE);

function bytesToSize1024($bytes, $precision = 2) {
$unit = array('B','KB','MB');
return @round($bytes / pow(1024, ($i = floor(log($bytes, 1024)))), $precision).' '.$unit[$i];
}

if (isset($_FILES['myfile'])) {
$sFileName = $_FILES['myfile']['name'];
$sFileType = $_FILES['myfile']['type'];
$sFileSize = bytesToSize1024($_FILES['myfile']['size'], 1);

echo <<<EOF
<div class="s">
<p>Le fichier : {$sFileName} a été correctement transféré.</p>
<p>Type : {$sFileType}</p>
<p>Taille : {$sFileSize}</p>
</div>
EOF;
} else {
echo '<div class="f">Une erreur s\'est produite</div>';
}

C'est le fichier côté serveur. Ce code ne sert évidemment pas à uploader mais permet de récupérer des informations sur les différents fichiers chargés (que nous afficherons à l'utilisateur à la fin du transfert).

VII - Conclusion

Nous avons vu dans cet article comment créer une interface user friendly pour transférer des fichiers multiples en utilisant du drag and drop. En espérant que cet article vous a aidé, n'hésitez pas à consulter les autres articles !

Voir un exemple en ligne / Télécharger l'archive de l'exemple.

VIII - Remerciements

Je tiens à remercier Andrey Prikaznov de m'avoir autorisé à traduire son tutoriel.
Je remercie également _Max_ pour sa relecture orthographique.