skip to Main Content

I’m trying to build input to upload multiple files and get response from server to update a UI.

I built something like this using jQuery:

function UploadFile(file) {
  return new Promise((resolve) => {
    $.ajax({
      url: "/api/admin/upload",
      type: "POST",
      data: file,
      processData: false,
      contentType: false,
      success: function (data) {
        var d = JSON.parse(data);
        if (d.error_info) {
          resolve({ r: "error", i: d.error_info });
        } else if (d.success_info) {
          resolve({ r: "success", i: "Ok" });
        } else if (d.fileexists) {
          resolve({ r: "fileexists", i: "Ok" });
        } else {
          resolve({ r: "error-0", i: data });
        }
      },
      error: function (data) {
        console.log(data);
        resolve({ r: "error-0", i: "ajax error" });
      },
    });
  });
}
$(document).on("change", "input", async function (e) {
  e.preventDefault();
  var files = $("input").prop("files");
  $.each(files, function (k, file) {
    //draw ui
  });
  $.each(files, async function (key, file) {
    var formData = new FormData();
    formData.append(`${key}`, file);
    let FileNo = key + 1;
    let TotalFiles = files.length;
    var result = await UploadFile(formData);
    console.log(result);
    switch (result.r) {
      case "success":
        StatusIco.html(
          '<i class="fa-solid fa-circle-check" data-info="Done"></i>'
        );
        break;
      case "error":
        StatusIco.html(
          `<i class="fa-solid fa-circle-xmark" data-info="${result.i}"></i>`
        );
        break;
      case "fileexists":
        StatusIco.html(
          '<i class="fa-solid fa-circle-exclamation" data-info="File exists"></i>'
        );
        break;
      default:
        console.log(result.i);
        StatusIco.html(
          '<i class="fa-solid fa-circle-xmark" data-info="Unknown error"></i>'
        );
        break;
    }
    //update total
    let percentage = Math.round((100 * FileNo) / TotalFiles) + "%";
    $(".uploading .percentage").html(percentage);
    $(".uploading .head .text").html(`Uploaded ${FileNo} from ${TotalFiles}`);
    //if last file
    if (FileNo == TotalFiles) {
      $(".uploading .head .text").html("Done");
    }
  });
});

Everything should work just fine, but sometimes number of total files + percentage wound get updated. Sometimes even my status icon will get stuck on uploading.

I need to upload them one by one because I have server file size limit and slow connection so if I upload more I’ll get 500 error or timed-out.

I tried to use promise.then but ended up with the same result.

Ty for help!

2

Answers


  1. You can use jQuery Form Plugin to help you.

    JQUERY

    $(function() {
    
        var bar = $('.bar');
        var percent = $('.percent');
        var status = $('#status');
    
        $('form').ajaxForm({
            beforeSend: function() {
                status.empty();
                var percentVal = '0%';
                bar.width(percentVal);
                percent.html(percentVal);
            },
            uploadProgress: function(event, position, total, percentComplete) {
                var percentVal = percentComplete + '%';
                bar.width(percentVal);
                percent.html(percentVal);
            },
            complete: function(xhr) {
                status.html(xhr.responseText);
            }
        });
    }); 
    

    HTML

    <form action="file-echo2.php" method="post" enctype="multipart/form-data">
        <input type="file" name="myfile"><br>
        <input type="submit" value="Upload File to Server">
    </form>
    
    <div class="progress">
        <div class="bar"></div >
        <div class="percent">0%</div >
    </div>
    
    <div id="status"></div>
    
    Login or Signup to reply.
  2. Your code does not upload the files one by one. Instead, it makes each http request for the files at once, and then each callback functions will return async (not in order). This happens because you’re using $.each(<iterable>, <function>), which if you await inside the asynced function, it does not wait to finish until it calls the next iteration. See this jsfiddle, but also see this

    Some suggestions:

    • you’d ideally want a server that handles the bandwidth (a cloud provider for blob storage) – are you sure you need to invest in the infrastructure to handle uploads by yourself?
    • implement rate limiting somewhere where you only implement it once, like extending the .ajax function to queue your requests. Keep in mind that client-side rate limiting is pointless if you don’t implement server-side rate limiting.
    • don’t set asynchronous: false on $.ajax. see the docs
    • in the link about js loops, keep in mind what Promise.all does (it does not run the async tasks in order)
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search