0

你好!

我想用 javascript / jquery 上传多个文件。

我的后端是 esp32。

当前代码仅使用一个文件,但无法上传多个文件。当数组中有多个文件时,它只上传一半的文件。

这是我的代码:

var promises = [];
//Gets called onchange
function dragAndDrop(el){
    promises = [];
    // Get all the files from the input element
    var files = $(el).prop('files');
    // Make sure there are files, but it's onchange so it must be.
    if(files){
        // add spinner icon to indicate the upload start to the user
        $("#uploadIndicatorIcon").html( '<i class="fas fa-spinner fa-spin"></i>' );
        // Check if the files fit in the filesystem
        if( !isFileArrFits(files) ){
            debug("error","There is not enough space for the files!");
            uploadingDone();
            return false;
        }
        // iterate trought all the files and upload them one by one.
        for (let file of files) {
            promises.push( uploadFile(file) );
        }
        // register the promises done from the ajax.
        updatePromises();
    }
}

function updatePromises(){
    $.when.apply(null, promises).done(function() {
        if(promises.length > 1){
            $('.file-input').prev().text("Files uploaded.");
            debug("success","Files uploaded.");
        }else{
            $('.file-input').prev().text("File uploaded.");
        }
        setTimeout(() => {uploadingDone();}, 100);
    })
}

// reset everything
function uploadingDone(){
    $("#fileUploadInput").val("");
    $("#uploadIndicatorIcon").empty();
    $('.file-input').prev().text("or drag and drop multiple files or a firmware");
}

function uploadFile(file){
    var url = "/uploading";
    if( file.name.includes("firmware") ){url = "/uploadingfirm"}
    form = new FormData();
    form.append("file", file, file.name);
    return $.ajax({
        url: url,
        type: 'POST',
        data: form,
        processData: false,
        contentType: false,
        async: true
    }).done(function(resp){
        if( file.name.includes("firmware") ){
            debug("success","Firmware uploading success. The module is restarting...");
            setTimeout(() => {location.reload();}, 6500);
        }else{
            debug("success",`Uploaded: ${file.name}`);
        }
    }).fail(function(resp){
        debug("error",`Failed: ${file.name}`);
    });
}

我认为问题出在这里:

for (let file of files) {
  promises.push( uploadFile(file) );
}
updatePromises();

由于 esp32 无法在这么长的时间内完成任务,我试图延迟这样的请求:

for (let file of files) {
  setTimeout(() => {
    promises.push( uploadFile(file) );
  }, 100); // tried also with 200,500 and 1000ms.
}
updatePromises();

我认为该updatePromises()函数是问题所在,因为它在 promises 数组填满之前被调用。所以我这样做了:

var filesIndex = 0;
for (let file of files) {
  setTimeout(() => {
    filesIndex++;
    promises.push( uploadFile(file) );
    if(filesIndex >= files.length){
       updatePromises();
    }
  }, 100); // tried also with 200,500 and 1000ms.
}

再次,没有成功。这可能是因为在我为 promise 注册回调之前文件开始上传。有趣的是,在每种情况下都会调用 updatePromises 函数,但文件总是只有一半。

如果我用这种完全相同的方法只上传一个文件,一切正常。

所以问题是。

我怎样才能延迟上传ajax?我是否应该为每个文件分别设置一个变量以指示它是否已上传并上传下一个?

*编辑:这是具有公认解决方案的工作代码:

function fileDragN_DropEvents(){
    var $fileInput = $('.file-input');
    var $droparea = $('.file-drop-area');
    $fileInput.on('dragenter focus click', function() {
      $droparea.addClass('is-active');
    });
    $fileInput.on('dragleave blur drop', function() {
      $droparea.removeClass('is-active');
    });
    $fileInput.on('change', function() {
      var filesCount = $(this)[0].files.length;
      var $textContainer = $(this).prev();
    
      if (filesCount === 1) {
        var fileName = $(this).val().split('\\').pop();
        $textContainer.text(fileName);
      } else {
        $textContainer.text(filesCount + ' files selected');
      }
    });
}

var promises = [];
function updatePromises() {
    $.when.apply(null, promises).done(function() {
        if(promises.length > 1){
            $('.file-input').prev().text("Files uploaded");
            debug("success","Files uploaded");
        }else{
            $('.file-input').prev().text("File uploaded");
        }
        setTimeout(() => {uploadingDone();}, 500);
    })
}

function uploadingDone(){
    $("#fileUploadInput").val("");
    $("#uploadIndicatorIcon").empty();
    $('.file-input').prev().text("or drag & drop multiple files or a firmware");
}

function dragAndDrop(el){
    promises = [];
    var files = $(el).prop('files');
    var promise = $.Deferred();
    promise.resolve();

    if( !isFileArrFits(files) ){
        debug("error","Files does not fit into the filesystem");
        uploadingDone();
        return false;
    }
    $("#uploadIndicatorIcon").html( '<i class="fas fa-spinner fa-spin"></i>' );

    for (let file of files) {
        promise = promise.then( () => uploadFile(file) );
        promises.push(promise);
    }
    updatePromises();
}

function uploadFile(file){
    var url = "/uploading";
    if( file.name.includes("firmware") ){url = "/uploadingfirm"}
    form = new FormData();
    form.append("file", file, file.name);
    return $.ajax({
        url: url,
        type: 'POST',
        data: form,
        processData: false,
        contentType: false,
        async: true
    }).done(function(resp){
        if( file.name.includes("firmware") ){
            debug("success","Firmware uploaded. Module restarts..");
            setTimeout(() => {location.reload();}, 6500);
        }else{
            debug("success",`Uploaded: ${file.name}`);
        }
    }).fail(function(resp){
        debug("error",`Failed: ${file.name}`);
    });
}
4

1 回答 1

0

这是一个只使用$.Deferred

就我个人而言,我永远不会使用$.Deffered,但是,这是一个挑战,我想我遇到了

// below this is just placehodler
var files = [1, 2, 3, 4, 5];

function uploadFile(v) {
  var p = $.Deferred();
  console.log(`${v} starting`);
  setTimeout(p.resolve, Math.random() * 1000 + 500, v);
  return p;
}

function updatePromises() {
  $.when.apply(null, promises).done(function() {
    console.log(arguments);
  })
}
var promises = [];
// above this is just placeholder
// this is the change
var promise = $.Deferred();
promise.resolve();
for (let file of files) {
  promise = promise.then(() => uploadFile(file)
    // not required, just shows sequence of events
    .then(r => {
      console.log(`${r} finished`);
      return `${r} done`;
    })
    // end of unrequired code
  );
  promises.push(promise);
}
// end of changes - the line below is the same as yours
updatePromises()
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

于 2021-06-29T13:06:50.960 回答