Question Chargement de fichiers par glisser-déposer sans AJAX, de manière synchrone au premier plan?


J'ai un site web avec un <input type="file"> upload de fichier, POST les données au backend lorsque le formulaire est soumis.

Je voudrais améliorer progressivement le formulaire afin que vous puissiez déposer un fichier depuis l'extérieur du navigateur n'importe où dans la fenêtre (pas seulement dans le champ de saisie des fichiers, comme dans certains navigateurs) pour le télécharger.

Que la forme soit ou non autosubmits n'est pas importante. Donc, si le glisser-déposer ne sélectionne que le fichier dans le champ de fichier, sans lancer de téléchargement, c'est bien. Je n'ai pas besoin de support pour plusieurs fichiers. Je n'ai pas besoin de montrer la progression du téléchargement, les vignettes ou tout autre élément intéressant.

Je sais qu'il existe des librairies JS qui prennent en charge les transferts par glisser-déposer, mais elles semblent toutes être téléchargées via AJAX. Je pourrais le faire, mais je devrais modifier le backend et le frontend pour gérer les erreurs de téléchargement, rediriger et afficher les bons messages sur le succès, etc.

Je veux une amélioration progressive qui ne nécessite aucun changement de backend. Cela devrait se produire de manière synchrone en utilisant le formulaire dans la page. JS va bien, tant que le téléchargement se passe "au premier plan". AJAX synchrone ne fonctionnerait pas, bien sûr.


14
2017-08-25 11:35


origine


Réponses:


Bien que n'étant pas vraiment "synchrone" (l'exécution de JavaScript ne s'arrêtera pas réellement), vous pouvez définir les fichiers sélectionnés par <input type="file"> par programmation. En fait, ces éléments et leur glisser partagent leur implémentation de backend de fichiers (File et FileList instances), il est donc très simple. Quoi de plus, en raison des deux frontends utilisant FileLists, le fait de faire glisser plusieurs fichiers fonctionne tout aussi simplement.

Cela fonctionne dans Chrome (en utilisant jQuery): http://jsfiddle.net/qMmPr/.

$(document).on("dragover drop", function(e) {
    e.preventDefault();  // allow dropping and don't navigate to file on drop
}).on("drop", function(e) {
    $("input[type='file']")
        .prop("files", e.originalEvent.dataTransfer.files)  // put files into element
        .closest("form")
          .submit();  // autosubmit as well
});

17
2017-08-25 13:07



Grâce au commentaire de @pimvdb, j'ai trouvé une solution assez élégante.

Depuis glisser et déposer sur le <input type="file" /> fonctionne, pourquoi ne pas le faire en plein écran sur dragstart pour s'assurer que l'utilisateur ne peut pas le manquer? De toute façon, il traîne si ses intentions sont claires en ce moment.

Voici une démo: https://jsfiddle.net/08wbo4um 

NB: malheureusement cela ne semble pas fonctionner dans un iframe, mais cela fonctionne sur une page réelle. Vous pouvez encore appréhender le comportement.

Voici l'extrait de code:

  $('input[type="file"]').on('change', function(e){
    var fileName = e.target.files[0].name;
    if (fileName) {
      $(e.target).parent().attr('data-message', fileName);
    }
  });
  
  $(document).on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    if ($('input[type="file"]').length) {
      if (['dragover', 'dragenter'].indexOf(e.type) > -1) {
        if (window.dragTimeout)
          clearTimeout(window.dragTimeout);
        $('body').addClass('dragged');
      } else if (['dragleave', 'drop'].indexOf(e.type) > -1) {
        // Without the timeout, some dragleave events are triggered
        // when the :after appears, making it blink...
        window.dragTimeout = setTimeout(function() {
          $('body').removeClass('dragged');
        }, 100);
      }
    }
  });
h3, p {
  text-align: center;
}

.form-group {
  margin: 30px;
}

.file-upload .form-control {
  height: 150px;
  outline: 1px dashed #ccc;
  outline-offset: -15px;
  background-color: #eee;
}
.file-upload .form-control:before {
  content: "\f093";
  font: normal normal normal 14px/1 FontAwesome;
  font-size: 3em;
  left: 0;
  right: 0;
  display: block;
  margin: 20px auto;
  text-align: center;
}
.file-upload .form-control:after {
  content: attr(data-message);
  left: 0;
  right: 0;
  bottom: 0;
  text-align: center;
  display: block;
}
.file-upload .form-control input[type="file"] {
  cursor: pointer;
  opacity: 0;
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 0;
  left: 0;
}
body.dragged .file-upload .form-control input[type="file"] {
  /* Make sure it is full screen, whatever the position absolute container */
  position: fixed;
  top: -50vh;
  bottom: -50vh;
  left: -50vw;
  right: -50vw;
  height: 200vh;
  width: 200vw;
  z-index: 10002;
}

body:after {
  content: 'You can drop the file. :-)';
  font-size: 2em;
  text-align: center;
  line-height: 100vh;
  position: absolute;
  top: 10px;
  bottom: 10px;
  left: 10px;
  right: 10px;
  background-color: #eee;
  z-index: 10000;
  border-radius: 4px;
  border: thin solid #ccc;
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s, opacity 0.5s ease;
}

body.dragged:after {
  opacity: 1;
  visibility: visible;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>

<h3>Drag N Drop file upload without AJAX Demo</h3>
<p>Try drag and dropping a file. :-)</p>

<div class="form-group file-upload" required="required">
    <label class="cols-sm-2 control-label" for="document_file">File Upload</label><br>
    <div class="cols-sm-10">
      <div class="input-group">
        <span class="input-group-addon"><i class="fa fa-file" aria-hidden="true"></i></span>
        <div class="form-control" data-message="Click to select file or drag n drop it here">
          <input required="required" title="Click to select file or drag n drop it here" type="file" name="document[file]" id="document_file">
        </div>
      </div>
    </div>
  </div>


0
2017-11-08 11:24



Cela peut être fait en transformant autoUpload en false, en collectant les fichiers dans un tableau, puis, sur le formulaire, faites un appel ajax avec tous les fichiers avec les données du formulaire, comme décrit ici.


-1
2018-06-01 06:07