Перетаскивание и отправка файлов для загрузки

Я-исполнителями перетащить файлы типа этой в asp.net в MVC 5, но мое требование - когда я перетаскиваю файлы, он не должен быть немедленно загружен. Сначала перетащите файлы, Затем нажмите на кнопка ("Enter MetaData") ввести несколько обязательных свойств (metadata-name, category & etc...) для каждого файла, Затем нажмите на другая кнопка (Отправить) отправить для отправки.

обычно, когда мы перетаскиваем файл, он сразу получает загружено, я должен остановить его и сделать это при нажатии кнопки(после заполнения других полей). Даже любая сторонняя библиотека js, которая имеет аналогичную функциональность ?

Я много гуглил, но не получал ожидаемых результатов. Может кто-то, пожалуйста, направьте меня, как пойти на это требование или предоставить мне некоторые ссылки для этого требования.

4 ответов


вы пробовали Dropzone.js http://www.dropzonejs.com/ Кажется, что у них есть поддержка функции, которую вы ищете. Ниже приводится выдержка из соответствующей страницы. Я сам не пробовал, но чисто на основе документации стоит попробовать проверить, соответствует ли она вашим требованиям.

Если вы отключили autoProcessQueue, вам нужно будет позвонить .processQueue() себя.

Это может быть полезно, если вы хотите, чтобы отобразить файлы и пусть пользователь нажмите кнопку Принять, чтобы фактически загрузить файл(ы).


пример кода, с которым вы связались, похоже, использует jquery.filedrop.js, который написан Weixi Yen. Вам нужно скачать и использовать последнюю версию от проект дома для этого, чтобы работать.

вы также должны загрузить и использовать более позднюю версию jquery, чем в комплекте с этим образцом кода. Я протестировал это с помощью jquery 1.9.1.

чтобы использовать расширение jquery, которое вы выбрали, вам нужно воспользоваться , и поставьте вашу собственную функцию. Вам также необходимо сохранить ссылки на done() функции, которые предоставляются вашей пользовательской функции для каждого файла, так что вы можете вызвать их позже, вызывая файлы для загрузки.

если вы хотите, чтобы для каждого файла появлялись мета-поля, вам нужно добавить соответствующий html для каждого файла, чтобы пользователь мог их заполнить.

резюме кода, который я бы предложил ниже:

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();
    });
});

С html похожим на следующий:

<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>

на div#perfile должен иметь css, чтобы скрыть его, так как он используется только для содержания div, который клонируется в форму каждый раз, когда файл перетаскивается.

Я создал доказательство концепции jsfiddle здесь, очевидно, это на самом деле не позволяет загружать файлы, но это показывает, что JS-сторона вещей работает. Вам нужно прокрутить вниз до нижней части панели javascript, чтобы увидеть пользовательский код - материал в верхней части просто включает расширение javascript вы должны были загрузить с домашнего сайта проекта.

Это действительно должно быть достаточно хорошо для вас, чтобы получить asp.net сторона дела работает. Вам просто нужно опубликовать дополнительные метаданные, предоставленные пользователем, так, как вы считаете нужным.

очевидно, что это голые кости, вы должны плоть его, как вы считаете нужным.


Так как вы ищете библиотеку, я разработал для вас полное решение vanilla Javascript следующим образом:

/* 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>

код выше полностью прокомментирован, но если вам нужна помощь, пожалуйста, оставьте комментарий.


согласно моим исследованиям, нет способа обработать загрузку вручную в С помощью jQuery.filedrop. Вы можете использовать функции событие как ниже запрос user для ввода некоторых дополнительных параметров, таких как метаданные и т. д... а затем прикрепите их к отправляющим данным следующим образом:

$('#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
    }
});