基于延迟承诺的多文件上传处理,在 for 循环中使用 javascript 到具有 ajax 的 esp32

Deferred promise based multiple file upload handling with javascript in a for loop to an esp32 with ajax

嗨!

我想用 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.
}

同样,没有成功。这可能是因为在我为承诺注册回调之前文件开始上传。有趣的是,在每种情况下都会调用 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}`);
    });
}

这里仅使用 $.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>