Josh Stodola
Josh Stodola

Reputation: 82483

Javascript Multiple File Upload, Sequentially One at a Time

We have a form with five <input type="file"/> elements that is in production and working great. We get request timeouts and MaxRequestLength exceeded errors on occasion. To prevent these errors, I planned to write some Javascript to upload the files one-at-a-time instead of all at once. Here is how I planned on doing this...

  1. On document.ready, inject a hidden iframe into page
  2. Change the <form> to target the iframe
  3. Disable all elements on the form (which prevents them from being POSTed)
  4. Enable one file-upload at a time and submit the form
  5. Wait for the response from the server
  6. When server response is printed into iframe, start the next upload
  7. When all uploads are done, refresh the page, which will invoke some server-side logic that populates a grid.

My problem is with number 5. Normally I think I could figure this out no problem, but I am just having one of those days where my brain is on strike. Here is my code thus far...

$(function() {
  $("<iframe/>").attr("src", "test.htm").attr("name", "postMe").hide().appendTo("body");
  $("form").attr("target", "postMe").submit(function(e) {
    e.preventDefault();
    $("#btnSubmit").attr("disabled", "disabled").val("Please Wait, Files are Uploading");

    for(var i = 1; i < 6; i++) {
      $("input[type=file]").attr("disabled", "disabled");
      $("#FileUpload" + i).removeAttr("disabled");
      $("form")[0].submit();
      // HELP!!!
      // How do I wait for server before next iteration?
    }

    location.reload(true);
  });
});

What kind of construct do I need here in order to "wait" for the server response before kicking off the next upload?

Upvotes: 1

Views: 6668

Answers (5)

krubo
krubo

Reputation: 6406

I was able to do this, by starting with the code at A Strategy for Handling Multiple File Uploads Using Javascript. That code uses an XMLHttpRequest for each file, but actually doesn't check the result from the server. I modified it to wait for the result from the server, sequentially, as follows:

var fileNumber = 0
var fileList = []    // see the code linked above for how to handle the fileList
var resultPane = document.getElementById('resultpane')   // a textarea box

sendNext = function() {
  if (fileNumber >= fileList.length) {
    resultPane.value += 'Done uploading '+fileNumber+' files\n'
    return 0
  }
  var formData = new FormData()
  var request = new XMLHttpRequest()
  request.onreadystatechange = function() {
    if (request.readystate == XMLHttpRequest.DONE) {
      resultPane.value += request.responseText    // show whatever the server said about each file
      sendNext()                                  // and send the next file
    }
  }
  formData.set('file', fileList[fileNumber])
  request.open('POST', 'https://example.com/upload-receiver')
  request.send(formData)
  resultPane.value += 'Sending file number '+fileNumber+'\n'
  fileNumber++
}

Upvotes: 0

Aleks
Aleks

Reputation: 55

It can be done with the help of jQuery's queue method and load event.

<!DOCTYPE HTML>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script>
//here's an upload script
(function($){
    //make 'em accessible within the scope
    var $iframe, $form;

    $(document).ready(function(){
        //create 'em only once, but use 'em many times
        $iframe = $('<iframe name="iframe" id="iframe" style="display:none"></iframe>').appendTo('body');       
        $form = $('<form method="post" enctype="multipart/form-data" target="iframe" style="display:none"></form>').appendTo('body');   
    });

    var iframeUpload = $({});

    $.iframeUpload = function(s){   
        iframeUpload.queue(function(next){
            //as we only wanna this new event
            $iframe.load(function(){
                //we must unbind the old one
                $iframe.unbind('load');

                //success or error, the question is up to you
                s.success();

                //but remember to remove or replace the old stuff
                $form.find('input').remove();

                next();
            });

            $form.attr('action', s.url).append(s.file).submit();
        });
    };
})(jQuery); 
//and this is how to use the script
(function($){$(document).ready(function(){
    $('input[type="submit"]').click(function(){
        $('input[type="file"]').each(function(){
            $.iframeUpload({
                url: 'http://example.com/upload.php',
                file: this,
                success: function(){
                    console.log('uploaded');
                }
            });
       });
    }); 
})})(jQuery);   
</script>
</head>
<body>   
    <!-- here are multiple files -->
    <input type="file" name="file" />
    <input type="file" name="file" />
    <input type="file" name="file" /> 
    <!-- to upload -->
    <input type="submit" />
</body>
</html>

Upvotes: 1

jbnunn
jbnunn

Reputation: 6355

I've had a lot of success lately using Uploadify--it's very configurable, free, and allows for multiple-uploads. It also provides the option for callback functions allowing you to really configure it any way you want.

http://www.uploadify.com/

Upvotes: 2

Dmitriy Likhten
Dmitriy Likhten

Reputation: 5206

Just FYI: jquery.forms plugin is all about making ajax form submitions. I use this plugin to submit a form (such as a file upload) in a separate iframe which the plugin takes care of automatically, and gives you a nice callback when completing.

This way most work for you is done.

http://jquery.malsup.com/form/

Upvotes: 1

Alex Ustinov
Alex Ustinov

Reputation: 100

I think you should listen for iframe's load event and perform input's switching in the handler. I completed with my own uploader today and this solution worked for me.

Upvotes: 1

Related Questions