javascript example Drag, Drop und Submit Dateien zum Hochladen



js file drag drop (4)

Ich implementiere Drag & Drop von Dateien wie folgt in asp.net mvc 5, aber meine Anforderung ist, wenn ich Dateien ziehe, sollte es nicht sofort hochgeladen werden. Ziehen Sie zuerst die Dateien, dann klicken Sie auf eine Schaltfläche ("Enter MetaData") , um einige obligatorische Eigenschaften (Metadaten-Name, Kategorie & etc ...) für jede Datei einzugeben, klicken Sie dann auf eine andere Schaltfläche (Senden) , um das Hochladen zu bestätigen.

Normalerweise, wenn wir eine Datei ziehen, wird sie sofort hochgeladen, ich muss sie stoppen und mache es auf Knopfdruck (nach dem Füllen der anderen Felder). Sogar jede dritte js Bibliothek, die ähnliche Funktionalität hat?

Ich habe viel gegoogelt, aber keine erwarteten Ergebnisse erzielt. Kann mir jemand bitte erklären, wie ich diese Anforderung erfüllen kann, oder mir einige Links zur Verfügung stellen, um diese Anforderung zu erfüllen.


Answer #1

Laut meinen Recherchen gibt es keine Möglichkeit, den Upload manuell in jquery.filedrop zu verarbeiten . Sie können eine der Ereignisfunktionen wie unten verwenden, um den Benutzer aufzufordern, zusätzliche Parameter wie Metadaten usw. einzugeben und sie dann wie folgt an die sendenden Daten anzuhängen:

$('#myElement').fileDrop({
    data: {
        param1: 'value1',           // send POST variables
        param2: function(){
            return calculated_data; // calculate data at time of upload
        },
    },
    onFileRead : function(fileCollection){
        $.each(fileCollection, function(){
            //Do stuff with fileCollection here!
        });
    },
    drop: function() {
        // user drops file
    },
    beforeSend: function(file, i, done) {
        // file is a file object
        // i is the file index
        // call done() to start the upload
    },
    // Called before each upload is started
    beforeEach: function(file){
        //do some stuff here
    }
});

Answer #2

Da Sie nach einer Bibliothek suchen, habe ich eine vollständige Javascript-Lösung für Sie entwickelt:

/* as soon as all the elements of the page has been loaded */
document.addEventListener('DOMContentLoaded', function() {
  /* we reference the most used elements into variables, making it easier to use later */
  var dropzone = document.getElementById('dropzone'),
      dialog = document.getElementById('dropzone-dialog'),
      submit = document.getElementById('submit'),
      progress = document.querySelector('progress'),
      _current = null; // we keep track what file the user is changing its metadata

  /* when the user drags something over our div, we must prevent the browser's default
     behavior and we change a CSS class only for usability reasons */
  dropzone.addEventListener('dragover', function(e) {
    e.preventDefault();
    dropzone.classList.add('dragging');
  });

  /* when the user leaves our div, we must remove the CSS class we put before */
  dropzone.addEventListener('dragleave', function(e) {
    e.preventDefault();
    dropzone.classList.remove('dragging');
  });

  /* that's the heart of our code,
     when the user effectively drops one or more files into the div */
  dropzone.addEventListener('drop', function(e) {
    /* again we must prevent the default browser's behavior,
       and we need to remove the CSS clas we put before */
    e.preventDefault();
    dropzone.classList.remove('dragging');
    submit.classList.add('hidden');

    /* we change our div's text */
    dropzone.textContent = 'File(s) dropped:';

    /* we create an UL element, so we can loop through the files make a list */
    var ul = document.createElement('ul');
    /* since the files property is not an array, but it is Array-like,
       we call the Array.prototype.forEach method passing the files to be the context */
    [].forEach.call(e.dataTransfer.files, function(file) {
      /* for each file the user has dropped, we create a LI and an anchor inside it */
      var li = document.createElement('li');
      var a = document.createElement('a');

      a.href = '#';
      a.textContent = file.name;
      a.title = 'Enter metadata';
      
      /* we create a property __file into our anchor to reference the dropped file */
      a.__file = file;

      li.appendChild(a);
      ul.appendChild(li);
    });
    dropzone.appendChild(ul);
  });

  /* here, we add a 'click' event handler to our div dropzone,
     so we can add the file's metadata behavior.
     the idea here is to make an event delegation, that is, when the user clicks anywhere
     inside the div, we see if the element that was clicked is one of the files */
  dropzone.addEventListener('click', function(e) {
    var el = e.target;

    /* if the element clicked is an anchor, it's because it's one of the files */
    if (el.tagName === 'A') {
      /* we prevent the browser's default behavior */
      e.preventDefault();
      /* we keep track that the current clicked file/anchor is the last clicked */
      _current = el;
      
      /* and then, we show our dialog, so the user can input the file's metadata */
      dialog.classList.remove('hidden');
      /* we set focus on the first element of the dialog, only for usability reasons */
      dialog.querySelector('#name').focus(); 
      
    }
  });

  /* we add an event handler to the dialog's close button,
     so it can effectively close the dialog */
  dialog.querySelector('.close').addEventListener('click', clearDialog);
  
  /* this is the second heart of our code.
     we add an event handler to the dialog's save button,
     so it can handle the saving of metadata */
  dialog.querySelector('#save').addEventListener('click', function() {
    /* here you can add any validation you want, e.g: required fields, etc */
    
    /* if everything was cool, we set a new property __dataFile to our anchor
       so, we can save the metadata for future use (the form submission) */
    _current.__dataFile = {
      name: dialog.querySelector('#name').value,
      category: dialog.querySelector('#category').value
      /* put here any other metadata property you will save */
    };
    
    /* when the user saves the metadata, we add a CSS class only for usability reasons */
    _current.classList.add('finished');

    /* then, we keep track if there is any file with no metadata saved yet */
    var btnsLeft = [].filter.call(dropzone.querySelectorAll('a'), function(el, i) {
      return !('__dataFile' in el);
    });

    /* if all the files' metadatas have been saved, we show the submit button */
    if (btnsLeft.length === 0)
      submit.classList.remove('hidden');

    /* and we clear/close our dialog */
    clearDialog();
  });

  function clearDialog() {
    /* when the dialog is closed, we set our current-clicked anchor to null */
    _current = null;

    /* we clean all the input fields of the dialog */
    [].forEach.call(dialog.querySelectorAll('input'), function(el, i) {
      el.value = '';
    });

    /* and we effectively hide/close the dialog */
    dialog.classList.add('hidden');
  }

  /* this is third heart of our code.
     we add a click event handler to our submit button,
     so we can effectively send our form to the server */
  submit.querySelector('button').addEventListener('click', function(e) {
    e.preventDefault(); // we must prevent the browser's default behavior

    /* we create a XMLHttpRequest to send our AJAX to the server */
    var xhr = new XMLHttpRequest();

    /* we add a 'load' event handler, so we can keep track when the upload is finished */
    xhr.addEventListener('load', finished);
    /* we do the same for progress and error, so we can inform our user what's going on */
    xhr.upload.addEventListener('progress', progress);
    xhr.addEventListener('error', error);
    
    /* now it's time to save all our data, so we create a FormData */
    var fd = new FormData();

    /* and for each anchor(file) inside our dropzone, we add new data to our FormData */
    [].forEach.call(dropzone.querySelectorAll('a'), function(el, i) {
      /* here we loop through our __dataFile previously created property,
         so we can add the file's metadata parameter */
      for (var prop in el.__dataFile) {
        /* since we are sending multiple files/metadatas,
           it's important to name our field with [] */
        fd.append(prop + '[]', el.__dataFile[prop]);
      }
      
      /* and then, we append the file itself - again, with the [] at the end */
      fd.append('file[]', e.__file);
    });

    /* now we open the xhr, and we effectively send the request to the server */
    xhr.open('POST', '/api/uploadfiles/'); // change for your API URL
    xhr.send(fd);
  });
  
  /* this is the xhr's progress, so we can update our HTMLProgressElement's percent */
  function progress(e) {
    progress.value = progress.innerHTML = (e.loaded / e.total * 100 | 0);
  }

  /* this is the xhr's finished event handler,
     we show a friendly message and hide the submit button */
  function finished(e) {
    progress.value = progress.innerHTML = 100;
    dropzone.innerHTML = '<h3>Files successfully uploaded</h3>';
    submit.classList.add('hidden');
  }
  
  /* this is the xhr's error event handler. If there is any known error, we show it */
  function error(e) {
    var xhr = e.target;
    submit.classList.add('hidden');
    dropzone.innerHTML = '<h3>Error while uploading</h3>' +
      (xhr.status ? '<span>' + xhr.status + ' - ' + xhr.statusText + '</span>' : '');
  }
});
#dropzone {
  width: 450px;
  height: 165px;
  background-color: #CCC;
  font: 16px Verdana;
  padding: 10px;
  box-sizing: border-box;
}
#dropzone.dragging {
  background-color: #EEE;
}
#dropzone > h3 {
  text-align: center;
}
#dropzone a {
  text-decoration: none;
}
#dropzone a:hover {
  text-decoration: underline;
}
#dropzone a.finished:after {
  content: ' √';
  color: green;
  font-weight: bold;
  text-decoration: none;
}
#dropzone ul {
  list-style: none;
  margin-left: 15px;
  padding: 0;
}
#dropzone li {
  margin-top: 8px;
}
#dropzone li:before {
  content: '> ';
}
.hidden {
  display: none;
}
.dialog {
  background-color: #AAAAFF;
  font-family: Verdana;
  padding: 12px;
  border-radius: 10px;
  width: 300px;
  height: 150px;
  position: absolute;
  top: 20px;
}
.dialog h3 {
  text-align: center;
}
.dialog .close {
  text-decoration: none;
  float: right;
}
.dialog .close:after {
  content: '✖';
  cursor: pointer;
}
.dialog > div {
  display: table;
}
.dialog > div > div {
  display: table-row;
}
.dialog > div > div > div,
.dialog > div > div > label {
  display: table-cell;
  padding: 2px 0 2px 10px;
}

#submit {
  height: 160px;
  width: 450px;
  text-align: right;
  position: fixed;
  top: 10px;
}
#submit button {
  font-size: 20px;
  padding: 10px;
  background-color: orange;
}

progress {
 width: 450px; 
}
<div id="dropzone">
  <h3>Drop your files here</h3>
</div>
<div>
  <progress min="0" max="100" value="0"></progress>
</div>
<div id="submit" class="hidden">
  <button>Submit</button>
</div>
<div id="dropzone-dialog" class="dialog hidden">
  <a class="close"></a>
  <h3>File Metadata</h3>
  <div>
    <div>
      <label for="name">
        Name
      </label>
      <div>
        <input id="name">
      </div>
    </div>
    <div>
      <label for="category">
        Category
      </label>
      <div>
        <input id="category">
      </div>
    </div>
    <div>
      <div></div>
      <div style="text-align: right">
        <button id="save">Save</button>
      </div>
    </div>
  </div>
</div>

Der obige Code ist vollständig kommentiert, aber wenn Sie Hilfe benötigen, hinterlassen Sie bitte einen Kommentar.


Answer #3

Der Beispielcode, den Sie verknüpft haben, scheint mit jquery.filedrop.js zu arbeiten, das von Weixi Yen geschrieben wurde. Sie müssen die neueste Version von Ihrem Projekt herunterladen und verwenden, damit dies funktioniert.

Sie sollten auch eine neuere Version von jQuery herunterladen und verwenden als die mit dem Beispielcode gebündelte Version. Ich habe das mit jquery 1.9.1 getestet.

Um die von Ihnen gewählte jQuery-Erweiterung zu verwenden, müssen Sie die Option vor dem beforeSend und eine eigene Funktion bereitstellen. Außerdem müssen Sie Verweise auf die done() -Funktionen speichern, die Ihrer benutzerdefinierten Funktion für jede Datei zur Verfügung gestellt werden, damit Sie sie später aufrufen können, wodurch die Dateien hochgeladen werden.

Wenn Sie möchten, dass für jede Datei Metaboxen angezeigt werden, müssen Sie den entsprechenden HTML-Code für jede Datei anhängen, damit der Benutzer sie ausfüllen kann.

Eine Zusammenfassung des Codes, den ich vorschlagen würde, ist unten:

var uploads_to_call = [];  // the global to store all the file upload callbacks

$('#dropzone').filedrop({
    fallback_id: 'upload_button',   // an identifier of a standard file input element, becomes the target of "click" events on the dropzone
    url: 'upload.php',              // upload handler, handles each file separately, can also be a function taking the file and returning a url
    // other important parameters related to upload, read the documentation for details

    // this is the important custom function you need to supply
    beforeSend: function(file, i, done) {
        // file is a file object
        // i is the file index
        // call done() to start the upload

        // this is just to clone meta boxes for the user to fill in for each file
        // it also fills in the filename so that it's obvious which meta boxes
        // are for which files
        $("#perfile").children().clone()
           .appendTo("#allmeta")
           .children(".filename").html(file.name);

        // this pushes the done() callback into the global mentioned earlier
        // so that we can call it later
        uploads_to_call.push(done);
    },
    afterAll: function() {
        // runs after all files have been uploaded or otherwise dealt with
        // you should possibly put code in here to clean up afterwards
    }
});

// set a handler to upload the files when the submit button is clicked
$("#submit").click(function(){
    $.each(uploads_to_call, function(i, upcall) {
        upcall();
    });
});

Mit HTML ähnlich wie folgt:

<form>
    <div id="dropzone"></div>
    <div id="allmeta"></div>
    <button id="submit">submit</button>
</form>

<div id="perfile">
    <div class="meta">
        <span class="filename"></span>
        <input type="text" placeholder="meta"/>
        <input type="text" placeholder="meta"/>
    </div>
</div>

Das div#perfile sollte css haben, um es zu verbergen, da es nur dazu verwendet wird, das div zu enthalten, das in das Formular geklont werden soll, wenn eine Datei hineingezogen wird.

Ich habe hier einen Proof of Concept jsfiddle erstellt , offensichtlich erlaubt dies nicht wirklich die Dateien hochgeladen werden, aber es zeigt die JS-Seite der Dinge funktioniert. Sie müssen bis zum Ende des Javascript-Steuerfelds scrollen, um den benutzerdefinierten Code zu sehen - das Zeug oben enthält nur die JavaScript-Erweiterung, die Sie von der Home-Website des Projekts heruntergeladen haben sollten.

Dies sollte wirklich gut genug für Sie, um die Asp.net-Seite der Dinge arbeiten zu bekommen. Sie müssen lediglich die vom Benutzer bereitgestellten zusätzlichen Metadaten in einer Weise veröffentlichen, die Sie für richtig halten.

Offensichtlich sind das Barebones, du solltest es so auslegen, wie du es für richtig hältst.


Answer #4

Haben Sie versucht Dropzone.js http://www.dropzonejs.com/ Es scheint, dass sie Unterstützung für eine Funktion haben, die Sie suchen. Nachfolgend finden Sie einen Auszug der relevanten Seite. Ich habe es nicht selbst ausprobiert, sondern rein auf Dokumentation basierend einen Versuch zu prüfen, ob es Ihren Anforderungen entspricht.

Wenn autoProcessQueue deaktiviert ist, müssen Sie selbst .processQueue() aufrufen.

Dies kann nützlich sein, wenn Sie die Dateien anzeigen möchten und der Benutzer auf eine Schaltfläche zum Akzeptieren klicken soll, um die Datei (en) tatsächlich hochzuladen.





drag-and-drop