web-dev-qa-db-fra.com

HTML5, JavaScript: Glissez et déposez un fichier à partir d'une fenêtre externe (Explorateur Windows)

Puis-je demander un bon exemple de mise en œuvre de HTML5 File Drag and Drop? Le code source devrait fonctionner si le glisser-déposer est effectué à partir d'une application externe (Explorateur Windows) vers la fenêtre du navigateur. Il devrait fonctionner sur autant de navigateurs que possible.

Je voudrais demander un exemple de code avec une bonne explication. Je ne souhaite pas utiliser de bibliothèques tierces, car je devrai modifier le code en fonction de mes besoins. Le code doit être basé sur HTML5 et JavaScript. Je ne souhaite pas utiliser JQuery.

J'ai passé toute la journée à chercher une bonne source de matériel, mais étonnamment, je n'ai rien trouvé de bon. Les exemples que j'ai trouvés fonctionnaient pour Mozilla mais ne fonctionnaient pas pour Chrome.

30
Bunkai.Satori

Ce lien explique ma question assez en détail:

http://www.html5rocks.com/en/tutorials/file/dndfiles/

15
Bunkai.Satori

Voici un exemple simple. Il montre un carré rouge. Si vous faites glisser une image sur le carré rouge, elle l’ajoute au corps. J'ai confirmé que cela fonctionne dans IE11, Chrome 38 et Firefox 32. Voir l'article Html5Rocks pour une explication plus détaillée.

var dropZone = document.getElementById('dropZone');

// Optional.   Show the copy icon when dragging over.  Seems to only work for chrome.
dropZone.addEventListener('dragover', function(e) {
    e.stopPropagation();
    e.preventDefault();
    e.dataTransfer.dropEffect = 'copy';
});

// Get file data on drop
dropZone.addEventListener('drop', function(e) {
    e.stopPropagation();
    e.preventDefault();
    var files = e.dataTransfer.files; // Array of all files

    for (var i=0, file; file=files[i]; i++) {
        if (file.type.match(/image.*/)) {
            var reader = new FileReader();

            reader.onload = function(e2) {
                // finished reading file data.
                var img = document.createElement('img');
                img.src= e2.target.result;
                document.body.appendChild(img);
            }

            reader.readAsDataURL(file); // start reading the file data.
        }
    }
});
<div id="dropZone" style="width: 100px; height: 100px; background-color: red"></div>
79
Dwayne

Examinez l'événement ondragover. Vous pouvez simplement avoir l'intérieur d'un div qui est caché jusqu'à ce que l'événement ondragover déclenche une fonction qui affichera le div avec le, permettant ainsi à l'utilisateur de glisser-déposer le fichier. Avoir une déclaration onchange sur le vous permettrait d'appeler automatiquement une fonction (comme le téléchargement) lorsqu'un fichier est ajouté à l'entrée. Assurez-vous que l'entrée autorise plusieurs fichiers, car vous n'avez aucun contrôle sur le nombre de fichiers qu'ils vont essayer de faire glisser dans le navigateur.

La réponse acceptée fournit n excellent lien pour ce sujet; cependant, selon les règles SO, les réponses de lien pur doivent être évitées car elles peuvent s'éteindre à tout moment. Pour cette raison, j'ai pris le temps de résumer le contenu du lien pour les futurs lecteurs.


Commencer

Avant de mettre en œuvre une méthode pour télécharger des fichiers sur votre site Web, vous devez vous assurer que les navigateurs que vous choisissez de prendre en charge seront capables de prendre pleinement en charge File API . Vous pouvez tester cela rapidement avec l'extrait de Javascript ci-dessous:

// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
  // Great success! All the File APIs are supported.
} else {
  alert('The File APIs are not fully supported in this browser.');
}

Vous pouvez bien sûr modifier l'extrait ci-dessus pour répondre à vos besoins.


Saisie de formulaire

La façon la plus courante de télécharger un fichier est d'utiliser l'élément standard <input type="file">. JavaScript renvoie la liste des objets File sélectionnés sous la forme d'un FileList.

  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.Push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '</li>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>

Glisser déposer

Apporter des modifications simples à l'extrait ci-dessus nous permet de fournir un support par glisser-déposer.

  function handleFileSelect(evt) {
    evt.stopPropagation();
    evt.preventDefault();

    var files = evt.dataTransfer.files; // FileList object.

    // files is a FileList of File objects. List some properties.
    var output = [];
    for (var i = 0, f; f = files[i]; i++) {
      output.Push('<li><strong>', escape(f.name), '</strong> (', f.type || 'n/a', ') - ',
                  f.size, ' bytes, last modified: ',
                  f.lastModifiedDate ? f.lastModifiedDate.toLocaleDateString() : 'n/a',
                  '</li>');
    }
    document.getElementById('list').innerHTML = '<ul>' + output.join('') + '</ul>';
  }

  function handleDragOver(evt) {
    evt.stopPropagation();
    evt.preventDefault();
    evt.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
  }

  // Setup the dnd listeners.
  var dropZone = document.getElementById('drop_zone');
  dropZone.addEventListener('dragover', handleDragOver, false);
  dropZone.addEventListener('drop', handleFileSelect, false);
<div id="drop_zone">Drop files here</div>
<output id="list"></output>

Lecture de fichiers

Maintenant que vous avez obtenu une référence au File, vous pouvez instancier un FileReader pour lire son contenu en mémoire. Une fois le chargement terminé, l'événement onload est déclenché et son attribut result peut être utilisé pour accéder aux données du fichier. N'hésitez pas à regarder les références pour FileReader pour couvrir les quatre options disponibles pour lire un fichier.

L'exemple ci-dessous filtre les images de la sélection de l'utilisateur, appelle reader.readAsDataURL() sur le fichier et affiche une miniature en définissant l'attribut src sur une URL de données.

  function handleFileSelect(evt) {
    var files = evt.target.files; // FileList object

    // Loop through the FileList and render image files as thumbnails.
    for (var i = 0, f; f = files[i]; i++) {

      // Only process image files.
      if (!f.type.match('image.*')) {
        continue;
      }

      var reader = new FileReader();

      // Closure to capture the file information.
      reader.onload = (function(theFile) {
        return function(e) {
          // Render thumbnail.
          var span = document.createElement('span');
          span.innerHTML = ['<img class="thumb" src="', e.target.result,
                            '" title="', escape(theFile.name), '"/>'].join('');
          document.getElementById('list').insertBefore(span, null);
        };
      })(f);

      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
  .thumb {
    height: 75px;
    border: 1px solid #000;
    margin: 10px 5px 0 0;
  }
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>

Tranchage

Dans certains cas, la lecture de l'intégralité du fichier en mémoire n'est pas la meilleure option. Par exemple, supposons que vous vouliez écrire un programme de téléchargement de fichiers asynchrones. Une façon possible d'accélérer le téléchargement serait de lire et d'envoyer le fichier dans des blocs de plage d'octets séparés. Le composant serveur serait alors chargé de reconstruire le contenu du fichier dans le bon ordre.

L'exemple suivant montre la lecture de morceaux d'un fichier. Il convient de noter qu'il utilise le onloadend et vérifie le evt.target.readyState Au lieu d'utiliser l'événement onload.

  function readBlob(opt_startByte, opt_stopByte) {

    var files = document.getElementById('files').files;
    if (!files.length) {
      alert('Please select a file!');
      return;
    }

    var file = files[0];
    var start = parseInt(opt_startByte) || 0;
    var stop = parseInt(opt_stopByte) || file.size - 1;

    var reader = new FileReader();

    // If we use onloadend, we need to check the readyState.
    reader.onloadend = function(evt) {
      if (evt.target.readyState == FileReader.DONE) { // DONE == 2
        document.getElementById('byte_content').textContent = evt.target.result;
        document.getElementById('byte_range').textContent = 
            ['Read bytes: ', start + 1, ' - ', stop + 1,
             ' of ', file.size, ' byte file'].join('');
      }
    };

    var blob = file.slice(start, stop + 1);
    reader.readAsBinaryString(blob);
  }
  
  document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
    if (evt.target.tagName.toLowerCase() == 'button') {
      var startByte = evt.target.getAttribute('data-startbyte');
      var endByte = evt.target.getAttribute('data-endbyte');
      readBlob(startByte, endByte);
    }
  }, false);
  #byte_content {
    margin: 5px 0;
    max-height: 100px;
    overflow-y: auto;
    overflow-x: hidden;
  }
  #byte_range { margin-top: 5px; }
<input type="file" id="files" name="file" /> Read bytes: 
<span class="readBytesButtons">
  <button data-startbyte="0" data-endbyte="4">1-5</button>
  <button data-startbyte="5" data-endbyte="14">6-15</button>
  <button data-startbyte="6" data-endbyte="7">7-8</button>
  <button>entire file</button>
</span>
<div id="byte_range"></div>
<div id="byte_content"></div>

Suivi des progrès

L'une des bonnes choses que nous obtenons gratuitement lors de l'utilisation de la gestion des événements asynchrones est la possibilité de surveiller la progression de la lecture du fichier; utile pour les fichiers volumineux, pour détecter les erreurs et pour déterminer quand une lecture est terminée.

Les événements onloadstart et onprogress peuvent être utilisés pour surveiller la progression d'une lecture.

L'exemple ci-dessous montre l'affichage d'une barre de progression pour surveiller l'état d'une lecture. Pour voir l'indicateur de progression en action, essayez un fichier volumineux ou un à partir d'un lecteur distant.

  var reader;
  var progress = document.querySelector('.percent');

  function abortRead() {
    reader.abort();
  }

  function errorHandler(evt) {
    switch(evt.target.error.code) {
      case evt.target.error.NOT_FOUND_ERR:
        alert('File Not Found!');
        break;
      case evt.target.error.NOT_READABLE_ERR:
        alert('File is not readable');
        break;
      case evt.target.error.ABORT_ERR:
        break; // noop
      default:
        alert('An error occurred reading this file.');
    };
  }

  function updateProgress(evt) {
    // evt is an ProgressEvent.
    if (evt.lengthComputable) {
      var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
      // Increase the progress bar length.
      if (percentLoaded < 100) {
        progress.style.width = percentLoaded + '%';
        progress.textContent = percentLoaded + '%';
      }
    }
  }

  function handleFileSelect(evt) {
    // Reset progress indicator on new file selection.
    progress.style.width = '0%';
    progress.textContent = '0%';

    reader = new FileReader();
    reader.onerror = errorHandler;
    reader.onprogress = updateProgress;
    reader.onabort = function(e) {
      alert('File read cancelled');
    };
    reader.onloadstart = function(e) {
      document.getElementById('progress_bar').className = 'loading';
    };
    reader.onload = function(e) {
      // Ensure that the progress bar displays 100% at the end.
      progress.style.width = '100%';
      progress.textContent = '100%';
      setTimeout("document.getElementById('progress_bar').className='';", 2000);
    }

    // Read in the image file as a binary string.
    reader.readAsBinaryString(evt.target.files[0]);
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
  #progress_bar {
    margin: 10px 0;
    padding: 3px;
    border: 1px solid #000;
    font-size: 14px;
    clear: both;
    opacity: 0;
    -moz-transition: opacity 1s linear;
    -o-transition: opacity 1s linear;
    -webkit-transition: opacity 1s linear;
  }
  #progress_bar.loading {
    opacity: 1.0;
  }
  #progress_bar .percent {
    background-color: #99ccff;
    height: auto;
    width: 0;
  }
<input type="file" id="files" name="file" />
<button onclick="abortRead();">Cancel read</button>
<div id="progress_bar"><div class="percent">0%</div></div>
2
PerpetualJ