Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

File uploads: Percentage completed progress bar

I'm trying to add a 'percentage completed so far' progress bar to avatar uploads in BuddyPress. The aim is to stop users navigating away from the page before the upload is completed.

The upload process is handled in BuddyPress by bp_core_avatar_handle_upload() in file bp-core/bp-core-avatars.php. The function starts off by checking that the file has been uploaded properly using bp_core_check_avatar_upload(). It then checks that the file size is within limits, and that it has an accepted file extension (jpg, gif, png). If everything checks out, the user is allowed to crop the image (uses Jcrop) and then the image is moved to its real location.

The actual upload is handled by the WordPress function wp_handle_upload.

How can I create a 'percentage completed' progress bar and display it when the file is uploading?

like image 832
henrywright Avatar asked Feb 08 '14 13:02

henrywright


3 Answers

I'm not familiar with BuddyPress, but all upload handlers (including the HTML5 XHR one that androbin outlined) will have a file progress hook point that you can bind to.

I've used uploadify, uploadifive and swfupload, and they can all interact with the same progress function handler in order to acheive the same progress bar result.

// SWFUpload
$elem.bind('uploadProgress', function(event, file, bytesLoaded) { fnProgress(file, bytesLoaded); })

// Uploadify
$elem.uploadify({ onUploadProgress: function (file, bytesUploaded, bytesTotal, totalBytesUploaded, totalBytesTotal) { fnProgress(file, bytesUploaded); });

// Uploadfive
$elem.uploadifive({ onProgress: function(file, e) { fn.onProgress(file, e.loaded); });

Uploadifive, being an HTML5 based uploader, simply binds to the XHR 'progress' event, so all these properties will be available to any HTML5 uploader.

As for the actual progress bar code..

HTML:

<div class='progressWrapper' style='float: left; width: 100%'>
    <div class='progress' style='float: left; width: 0%; color: red'></div>
    <div class='progressText' style='float: left;></div>
</div>

JS:

var fnProgress = function(file, bytes) {
    var percentage = (bytesLoaded / file.size) * 100;

    // Update DOM
    $('.progress').css({ 'width': percentage + '%' });
    $('.progressText').html(Math.round(percentage + "%");
}
like image 97
Stumblor Avatar answered Nov 03 '22 17:11

Stumblor


You should use an XHR object. I don't now if it helps you, but I have a simple XHR uploader written.

HTML:

    <form id="uploader" enctype="multipart/form-data" action="uploadimage.php" method="post">
        <input type="file" id="file" name="file[]" multiple="multiple" accept="image/jpeg" /><br/>
        <input type="submit" value="Upload" />
        <div class="list" style="background-color:#000;color:#FFF;padding:5px;display:none;border-radius:5px;">
        </div>
    </form>

JS:

$("#uploader").submit(function(){
    $('#uploader .list').fadeIn(100).css("width","0px");
    var data = new FormData();
    // if you want to append any other data: data.append("ID","");
    $.each($('#file')[0].files, function(i, file) {
        data.append('file-'+i, file);
    });
    $.ajax({
        url: 'uploadimage.php',
        data: data,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        xhr: function() {  // custom xhr
            myXhr = $.ajaxSettings.xhr();
            if(myXhr.upload){ // check if upload property exists
                myXhr.upload.addEventListener('progress',progressHandlingFunction, false); // for handling the progress of the upload
            }
            return myXhr;
        },
        success: function(data2){
            $('#uploader .list').css({
                "width":"200px",
                "text-align":"center",
                "margin":"10px 0 10px 0"
            }).html("DONE!").delay(2000).fadeOut(500);
            if (data2 == "ERROR_FILESIZE"){
                return alert("Choose another file");
            }
                            else{ /*change location*/ }
        });
    return false;
});

In this case I uploaded the file with uploadimage.php and if it printed: "ERROR_FILESIZE" then it alerted the error.

like image 29
androbin Avatar answered Nov 03 '22 17:11

androbin


I think that before you worry about the client-side of things you should be aware of the server-side requirements to actually be able to accomplish this.

For PHP you need to have the session.upload_progress enabled unless the wp_handle_upload() function uses something different, so I'm here just guessing, but chances are they do use the regular PHP session stuff hence it needs to be enabled.

If you look at the comments for the given link many users say that progress state does not work under certain environments such as PHP on FastCGI which is what you'll get in shared hosting environments most of the time.

Now many people here are telling you to use the XHR uploader but the problem is that they are giving you an example of a custom upload.php script or something like that to send the data, but you are using a wordpress plugin which you don't control (kinda)

So considering that the wp_handle_upload() does not actually works in an AJAX way then you would have to hook an event when the file upload form submit button is clicked and set a timer in JS which calls some URL where you pass the form data like an ID, and then query the session with that ID to check the progress of the file:

 $_SESSION["upload_id"]["content_length"]
 $_SESSION["upload_id"]["bytes_processed"]

With that data you can calculate how much has been transfered. You could set the JS timer to be called like each second but if the files they are uploading are not very large (say, larger than 1mb) and they have a good connection then there won't be much progress to be notified.

Check this link for a step by step example on how to work with this session upload data.

like image 3
Gustavo Rubio Avatar answered Nov 03 '22 17:11

Gustavo Rubio