Question

I have a multiple files upload form:

<input type="file" name="files" multiple />

I am posting these files with ajax. I would like to upload the selected files one by one (to create individual progress bars, and out of curiousity).

I can get the list of files or individual files by

FL = form.find('[type="file"]')[0].files
F  = form.find('[type="file"]')[0].files[0]

yieling

FileList { 0=File, 1=File, length=2 }
File { size=177676, type="image/jpeg", name="img.jpg", more...}

But FileList is immutable and I can't figure out how to submit the single file.

I think this is possible as I saw http://blueimp.github.com/jQuery-File-Upload/. I don't want to use this plugin however as it's as much about learning as the result (and it would need too much custimizing anyway). I also don't want to use Flash.

Était-ce utile?

La solution

For this to be a synchronous operation, you need to start the new transfer when the last one is done. Gmail, for example, send everything at once, concurrently. The event for the progress on AJAX file upload is progress or onprogress on the raw XmlHttpRequest instance.

So, after each $.ajax(), on the server side (which I don't know what you'll be using), send a JSON response to execute the AJAX on the next input. One option would to bind the AJAX element to each element, to make things easier, so you could just do, in the success the $(this).sibling('input').execute_ajax().

Something like this:

$('input[type="file"]').on('ajax', function(){
  var $this = $(this);
  $.ajax({
    'type':'POST',
    'data': (new FormData()).append('file', this.files[0]),
    'contentType': false,
    'processData': false,
    'xhr': function() {  
       var xhr = $.ajaxSettings.xhr();
       if(xhr.upload){ 
         xhr.upload.addEventListener('progress', progressbar, false);
       }
       return xhr;
     },
    'success': function(){
       $this.siblings('input[type="file"]:eq(0)').trigger('ajax');
       $this.remove(); // remove the field so the next call won't resend the same field
    }
  });
}).trigger('ajax');  // Execute only the first input[multiple] AJAX, we aren't using $.each

The above code would be for multiple <input type="file"> but not for <input type="file" multiple>, in that case, it should be:

var count = 0;

$('input[type="file"]').on('ajax', function(){
  var $this = $(this);
  if (typeof this.files[count] === 'undefined') { return false; }

  $.ajax({
    'type':'POST',
    'data': (new FormData()).append('file', this.files[count]),
    'contentType': false,
    'processData': false,
    'xhr': function() {  
       var xhr = $.ajaxSettings.xhr();
       if(xhr.upload){ 
         xhr.upload.addEventListener('progress', progressbar, false);
       }
       return xhr;
     },
    'success': function(){
       count++;
       $this.trigger('ajax');
    }
  });
}).trigger('ajax'); // Execute only the first input[multiple] AJAX, we aren't using $.each 

Autres conseils

This looks like a very good tutorial, just what you're looking for.

That noted, uploading via vanilla ajax isn't supported in all browsers, and I would still recommend using an iframe. (I.E dynamically create an iframe and POST it using javascript)

I have always been using this script and I find it very concise. If you want to learn how to upload via creating an iframe, you should dig through it's source.

Hope it helps :)

Extra edit

To create progress bars with the iframe method, you will need to do some work server-side. If you're using php, you can use:

If use nginx you can also choose to compile with their Upload progress module

All of these work the same way - every upload has a UID, you will request for the 'progress' associated with that ID at consistent intervals via ajax. This will return the upload progress as determined by the server.

I was facing same problem and came with this solution. I simply retrieve form with multipart, and in recursive call ( file after file ) start ajax request, which only calls next when is done.

var form = document.getElementById( "uploadForm" );
var fileSelect = document.getElementById( "photos" );
var uploadDiv = document.getElementById( "uploads" );

form.onsubmit = function( event ) {
    event.preventDefault(  );

    var files = fileSelect.files;
    handleFile( files, 0 );
};

function handleFile( files, index ) {
    if( files.length > index ) {
        var formData = new FormData(  );
        var request = new XMLHttpRequest(  );

        formData.append( 'photo', files[ index ] );
        formData.append( 'serial', index );
        formData.append( 'upload_submit', true );

        request.open( 'POST', 'scripts/upload_script.php', true );
        request.onload = function(  ) {
            if ( request.status === 200 ) {
                console.log( "Uploaded" );
                uploadDiv.innerHTML += files[ index ].name + "<br>";
                handleFile( files, ++index );
            } else {
                console.log( "Error" );
            }
        };
        request.send( formData );
    }
}

Using this source code you can upload multiple file like google one by one through ajax. Also you can see the uploading progress

HTML

 <input type="file" id="multiupload" name="uploadFiledd[]" multiple >
 <button type="button" id="upcvr" class="btn btn-primary">Start Upload</button>
 <div id="uploadsts"></div>

Javascript

<script>

function uploadajax(ttl,cl){

var fileList = $('#multiupload').prop("files");
$('#prog'+cl).removeClass('loading-prep').addClass('upload-image');

var form_data =  "";

form_data = new FormData();
form_data.append("upload_image", fileList[cl]);


var request = $.ajax({
          url: "upload.php",
          cache: false,
          contentType: false,
          processData: false,
          async: true,
          data: form_data,
          type: 'POST', 
          xhr: function() {  
      var xhr = $.ajaxSettings.xhr();
      if(xhr.upload){ 
        xhr.upload.addEventListener('progress', function(event){
                        var percent = 0;
                        if (event.lengthComputable) {
                            percent = Math.ceil(event.loaded / event.total * 100);
                        }
                        $('#prog'+cl).text(percent+'%')

                    }, false);
       }
      return xhr;
     }    
      })
      .success(function(res,status) {

        if(status == 'success'){

            percent = 0;
            $('#prog'+cl).text('');            

                $('#prog'+cl).text('--Success: ');

            if(cl < ttl){
                uploadajax(ttl,cl+1);
            }else{
               alert('Done ');

            }     

            }

      })
      .fail(function(res) {
      alert('Failed');
      });

}



$('#upcvr').click(function(){

var fileList = $('#multiupload').prop("files");
$('#uploadsts').html('');
var i
for ( i = 0; i < fileList.length; i++) {
$('#uploadsts').append('<p class="upload-page">'+fileList[i].name+'<span class="loading-prep" id="prog'+i+'"></span></p>');
if(i == fileList.length-1){
    uploadajax(fileList.length-1,0);
}
}

});

</script>

PHP

upload.php
    move_uploaded_file($_FILES["upload_image"]["tmp_name"],$_FILES["upload_image"]["name"]);

Html Code

<div class="row">
    <form id="singleUploadForm" name="singleUploadForm" style="float: right; padding-right: 25px">
        <input style="float: left;" id="singleFileUploadInput" type="file" name="files" class="file-input btn btn-default" required multiple/>
        <button style="float: left" type="submit" class="btn .btn-primary">Upload</button>
    </form>
</div>

JavaScript Code

$('#mutipleUploadForm').submit(function (event) {
    var formElement = this;
    var formData = new FormData(formElement);
    $.ajax({
        type: "POST",
        enctype: 'multipart/form-data',
        url: "/uploadFile",
        data: formData,
        processData: false,
        contentType: false,
        success: function (response) {
            alert("Image Uploaded Successfully");

            location.reload();
        },
        error: function (error) {
            console.log(error);
            // process error
        }
    });
    event.preventDefault();
});

Spring Controller Class

@PostMapping("/uploadFile")
@ResponseBody
public boolean uploadMutipleFiles(@RequestParam("file") MultipartFile[] file) {
    List<Boolean> uploadStatusList = new ArrayList<>();
    for (MultipartFile f : file) {
        uploadStatusList.add(uploadFile(f));
    }
    return !uploadStatusList.contains(false);
}

public boolean uploadFile(@RequestParam("file") MultipartFile file) {
    boolean uploadStatus = galleryService.storeFile(file);
    return uploadStatus;
}

Spring Service Class

public boolean storeFile(MultipartFile file) {
    boolean uploadStatus = false;
    try {
        String fileName = file.getOriginalFilename();
        InputStream is = file.getInputStream();
        Files.copy(is, Paths.get(imagesDirectory + fileName), StandardCopyOption.REPLACE_EXISTING);
        System.out.println("File store success");
        uploadStatus = true;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return uploadStatus;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top