HTML5 multiple file upload: upload one by one through AJAX
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"]);
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:
- Session upload progress for php5.4+
- Upload progress package for php<5.4
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.
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