Dustin Dustin - 4 months ago 12
PHP Question

Update multiple progress bars when posting multiple files and form elements via ajax

How do you update multiple progress bars when posting a form via ajax? Here is the code I have but I can't figure it out:

Form code:

<form id="upload-form" class="no-padding" method="post" enctype="multipart/form-data">
<p><label for="folder">Create folder:</label><input type="text" name="folder" placeholder="Enter a folder name"></p>
<p><label for="file">Create file:</label><input type="text" name="file" placeholder="Enter a file name with extension (e.g. home.php)"></p>
<p class="no-margin">Upload file(s):</p>
<div class="custom-upload">
<input id="upload" type="file" name="upload[]" multiple>
<div class="fake-file">
<a class="browse text-center"><i class="fa fa-list"></i> Browse</a><input placeholder="No file(s) selected..." disabled="disabled" >
</div>
</div>
<div id="selectedFiles" class='selectedFiles'></div>
<?php echo "<input name='url' hidden value='" . $_SERVER['REQUEST_URI'] ."'>";?>
<button id="submit" name="submit"><i class="fa fa-upload"></i> Upload</button>
<p id="uploading" class='success text-right' hidden>Please be patient while your files are uploading.</p>
</form>


Javascript code:

var selDiv = "";
document.addEventListener("DOMContentLoaded", init, false);
function init() {
document.querySelector('#upload').addEventListener('change', handleFileSelect, false);
selDiv = document.querySelector("#selectedFiles");
}

var files, filesToUpload;

// populates files into array (filesToUpload) and displays selected files to the user
function handleFileSelect(e) {
if(!e.target.files) return;
selDiv.innerHTML = "";
var files = e.target.files;
filesToUpload = Array.prototype.slice.call(files);
if (files.length > 0) {
selDiv.innerHTML += '<p id="file-upload-paragraph" class="no-padding no-margin">Files selected for upload. Click the <b>x</b> to cancel file upload for a specific file:</p>';
}
for(var i = 0; i < files.length; i++) {
var f = files[i];
selDiv.innerHTML += '<div class="selectedFiles"><a href="#" class="cancel text-center"><i class="fa fa-remove"></i></a><progress id="progress' + i + '" class="text-right" value="0" hidden></progress><span class="file-holder">' + f.name + ' <i class="fa fa-file"></i></span></div>';
}
}

// removes user selected file before upload
$(document).on('click', '.cancel', function () {
filesToUpload.splice($(".cancel").index(this), 1);
$(this).closest('div').remove();
if (filesToUpload.length == 0) {
$('#file-upload-paragraph').remove();
$('.custom-upload input[type=file]').val('');
}
$('.custom-upload input[type=file]').next().find('input').val(filesToUpload.length + ' files selected!');
});

// sets progress bar for each loaded file
$('#upload-form').submit(function(e){
e.preventDefault();
var url = location.href;
$("#upload").remove();
$(".cancel").hide();
$("progress").show();
var data = new FormData($('form')[0]);
if (filesToUpload.length != 0) {
for (var i = 0, j = filesToUpload.length; i < j; i++) {
data.append("upload[]", filesToUpload[i]);
}
}
$.ajax({
url: url,
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false,
xhr: function(progress) {
for (var i = 0, j = filesToUpload.length; i < j; i++) {
var progressBar = 'progress' + i;
if(document.getElementById(progressBar) == null) {
j++;
continue;
}
var xhr = new XMLHttpRequest();
(function(progressBar) {
xhr.upload.onprogress = function(e) {
$('#' + progressBar).attr({value: e.loaded, max: e.total});
};
}(progressBar));
}
return xhr;
},
success: function(res){
if(!res.error) location.reload(true);
}
});
});


PHP code:

// function call
uploadFiles(count($_FILES["upload"]["name"]), $_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $path);

// function that uploads selected files
function uploadFiles($total, $tmpFiles, $uploadedFiles, $path) {
for($i=0; $i < $total; $i++) {
$tmpFilePath = $tmpFiles[$i];
if ($tmpFilePath != ""){
$newFilePath = "$path/" . $uploadedFiles[$i];
if(file_exists($newFilePath)) {
unlink($newFilePath);
}
move_uploaded_file($tmpFilePath, $newFilePath);
}
}
}


Here is a picture of the form, just in case:
Form image

Thanks in advance for any help.

Answer

I don't know if anyone will be looking to do the same thing but I found my own answer. Basically, replace my initial code for catching when the form is submitted with the following:

// sets progress bar for each loaded file
$('#upload-form').submit(function(e){
    e.preventDefault(); // removes the default behavior of the form button
    var url = location.href; // returns the current location
    $("#upload").remove(); // removes the file upload box which is replaced with an array in my other code
    $(".cancel").hide(); // hides the cancel buttons
    $("progress").show(); // shows the hidden progress bars

    // checks to see if files were selected for being uploaded
    if (filesToUpload.length != 0) {
        var progressBars = [];

        // this loop accounts for files that were deleted after selection
        for (var i = 0, j = filesToUpload.length; i < j; i++) {
            if(document.getElementById('progress' + i) == null) {
                j++;
                continue;
            }
            progressBars.push(i);
        }

        // call to postNext function
        postNext(0, progressBars, url);

    // executes when only the other form elements are submitted with no file upload
    } else {
        var data = new FormData($('form')[0]);
        $.ajax({
            url: url,
            type: 'POST',
            data: data,
            cache: false,
            contentType: false,
            processData: false,
            success: function(res){
                if(!res.error) location.reload(true);
            }       
        });
    }
});

// posts each file separately
function postNext(i, progressBars, url) {

    // continues as long as there are more files to display progress bars
    if (i < progressBars.length) {
        var index = progressBars[i];
        var data = new FormData($('form')[0]);

        // after first ajax send, resets form so only the remaining file uploads are resubmitted
        if (i == 0) {
            $("#upload-form")[0].reset();
        }
        data.append("upload[]", filesToUpload[i]); //append the next file
        $.ajax({
            url: url, // url for post
            type: 'POST',
            data: data, 
            cache: false,
            contentType: false,
            processData: false,
            xhr: function(progress) { 
                    // set the progress for a given progress bar
                    var xhr = new XMLHttpRequest();
                    var progressBar = 'progress' + index;
                    (function(progressBar) { 
                        xhr.upload.onprogress = function(e) {
                            $('#' + progressBar).attr({value: e.loaded, max: e.total});
                        };
                    }(progressBar));
                return xhr;
            },
            beforeSend: postNext(i + 1, progressBars, url) // begins next progress bar
        });
    }
}

// refreshes the page only after all ajax requests are completed
$(document).bind("ajaxSend", function () {
    console.log("waiting for all requests to complete...");
}).bind("ajaxStop", function () {
    location.reload(true);
});
Comments