Upload progress bar in CodeIgniter without Flash

Today we will see how we can create a progress bar file upload in CodeIgniter without the need for libraries or plug-ins made with Flash (like SWFUpload) that are beyond our control, because usually these libraries offer us already compiled .swf files, so we can not change anything in them, just in case we have knowledge of programming in ActionScript 2 or 3.

First of all, we have to say that creating a progress bar file upload in PHP is not as easy as it seems. The first problem is that PHP versions prior to 5.2 do not provide the necessary tools to provide information about the file upload at every moment. The second problem is that AJAX, by itself, does not allow us to check the status of the file upload process, since, for obvious security reasons, JavaScript does not have access to files on the client operating system, so you need a “trick” using an iframe.

1. Setting up our server

To implement our upload progress bar we need our PHP server to be in the version 5.2 or higher (in PHP 5.5 the APC module is already included in the core, so you do not need to install it as a separate module). The next thing we need is to have installed the module APC (Alternative PHP Cache) on our PHP server. This module will allow us to know the status of the file uploading process at all times. In order to check if we have APC already installed on our server we can write the following code in any php file that we have on the server:

<?php phpinfo(); ?>

If everything is fine, we should see that our server version is 5.2 or later and the APC module is installed and has the following parameters and values:

  • apc.rfc1867 = On
  • apc.rfc1867_freq = 5k

With this we would have prepared our server to report the status of the file upload.

2. The form to upload the file

Now we prepare the file upload form that we need for the progress bar. For that we have to create a view in CodeIgniter that will contain the form, but we are not going to use any Form_helper function for this, we are going to use just pure HTML code:

<?php $upload_id = random_string("alnum", 11); ?>
<form name="uploadForm" id="uploadForm" action="<?php echo base_url(); ?>upload/upload_file" method="POST" enctype="multipart/form-data" target="uploadIframe" onsubmit="startProgress('<?php echo $upload_id;?>');">
<input type="hidden" name="APC_UPLOAD_PROGRESS" id="progress_key" value="<?php echo $upload_id;?>"/>
<input type="file" name="uploadedFile" size="50" id="uploadedFile"/>
<span>(max. 100 MB)</span>
<br />
<input type="submit" value="Upload" />
</form>
<br />
<div id="progressBar"></div>
<iframe id="uploadIframe" name="uploadIframe" src=""></iframe>
<script type="text/javascript">var url = "<?php echo base_url();?>";</script>
<script type="text/javascript" src="<?php echo base_url();?>js/uploadprogress.js"></script>

As we can see, finally we added the path of the JavaScript file to manage the progress bar using AJAX queries. It is a standard HTML form except for the following features:

  • It is sent to an iframe. This is necessary so the JavaScript code can continue running while the file is being uploaded, otherwise the page would be reloaded and the JavaScript (AJAX) code execution would be lost. When we process the form and the file is uploaded, the iframe is the place where we will show the response that the “upload_file” function will return (which is the function that will process the form).
  • There is a special hidden field called “APC_UPLOAD_PROGRESS” with a unique value that was generated previously in the controller. This value is also passed to the function that monitors the status of the file uploading and it allows us to identify the file from which we get the status of the uploading process. It is important that this hidden field is always set before the field of type “file” on the form.
  • There is a div called “progressBar” where we will show the progress bar.
  • We add the “onsumbit” event to the form, so that once the sending of the form is started, the JavaScript function startProgress is called. This function will be the one that will control file uploading with AJAX.

3. Styling the progress bar

This is where lies one of the big advantages of this method, and it is to be able to change the layout and colours, sizes, etc. of the components of the bar as we want and how it best suits our site. In the .css file where we want to style the progress bar we add the following rules:

iframe#uploadIframe {
 border: 0px none;
 display: none;
 width: 500px;
}
.progressGrey {
 background: #ccc;
 border:1px solid #000;
 color: #666;
 font-size: 13px;
 font-weight:bold;
 height: 15px;
 overflow:hidden;
 padding:1px 0px 1px 3px;
 position:relative;
 white-space:nowrap;
 width: 500px;
}
.progressRed {
 background: #f00;
 border-right:1px solid #000;
 color: #fff;
 font-size: 13px;
 font-weight:boldwhite-space:nowrap;
 height: 15px;
 left:0px;
 overflow:hidden;
 padding:1px 0px 1px 3px;
 position:absolute;
 top:0px;
 white-space:nowrap;
}

Feel free to change colours, sizes, fonts and whatever you want for your site.

4. Our function for reporting the status and the function that processes the uploading

We now create the function that will handle the state of the file upload at all times. For this, we need a controller in CodeIgniter called “upload” and inside we will have a function called “json_get_uploadprogress”. The reason for the name begins with “json_” is because use this “convention” to indicate that the response returned by this function will be in JSON format. The code would be:

public function json_get_uploadprogress()
{
    $data = array();
    if($this->input->post('upload_id'))
    {
        $uploadKey = $this->input->post('upload_id');
        $status = false;
        $percentage = 0;
        $result = "INITIALISING UPLOAD";
        $data["percentage"] = $percentage;
        $data["result"] = $result;
        if (function_exists('apc_fetch'))
        {
            $status = apc_fetch('upload_'.$uploadKey);
        }
        if (is_array($status))
        {
            log_message("error", "STATUS: ".print_r($status,TRUE));
            if (array_key_exists("total", $status) && array_key_exists("current", $status))
            {
                $percentage = round((($status['current'] / $status['total']) * 100),2);
                if ($status['current'] >= $status['total'])
                {
                    $percentage = 100;
                    $result = "UPLOAD COMPLETE";
                }
                else
                {
                    $bytes = array('B','KB','MB','GB','TB');
                    foreach($bytes as $val)
                    {
                        if($status["total"]  > 1024)
                        {
                            $status["total"] = $status["total"] / 1024;
                            $status["current"] = $status["current"] / 1024;
                        }
                        else
                        {
                            break;
                        }
                    }
                    $result =  $percentage."% (".round($status["current"], 2)." ";
                    $result.= $val." of ".round($status["total"], 2)." ".$val.")";
                }
                $data["percentage"] = $percentage;
                $data["result"] = $result;
            }

        }
    }
    $this->load->view('json/json_response_view',array("array" => $data));
}

In the view “json_response_view” we only have the following statement:

<?php echo json_encode($array); ?>

This is the function that we will call from AJAX so it reports to us the status of the file upload.

Now we will see a function in the same controller that is responsible for collecting and processing the form file upload. This function will return a response when finished, which will be shown in the iframe form.

public function upload_file()
{
    $path_file = "files/";

    if(!is_dir($path_file))
    {
        mkdir($path_file);
    }

    $config['upload_path'] = $path_file;
    $config['allowed_types'] = 'mp4';
    $size_MB = 100; //Max size of the file in MB
    $config['max_size'] = $size_MB * 102400; //
    $this->load->library('upload');
    $this->upload->initialize($config);
    $div_start = "
“; $div_end = “

“; if (!$this->upload->do_upload(‘uploadedFile’)) { $this->data[“params”] = array(‘success_msg’ => $this->upload->display_errors()); log_message(‘error’,’El error: ‘.$this->upload->display_errors()); } else { $uploaded_file = $this->upload->data(); $this->load->view(“ajax/generic_response_view”,array(“resp” => $div_start.”The file has been uploaded successfully.”.$div_end)); return; } }

5. AJAX with jQuery

Finally, we only need to create the JavaScript file that queries the status of the file uploaded via AJAX with jQuery. In our case, the query is performed every 1 second, you can modify this time as you need it, but you must take into account that if you perform too many queries in a short period of time (below 1 sec), because you can overload the client browser or cause that queries and responses crush each others if the client connection is not very good. In the other hand, if the query time is too large (above the second) it may cause the user to receive very sporadic feedback on the state of the uploading process, giving the feeling to the user that the climb is done “leapfrog”.

This is the jQuery code you will need to place in the “js/uploadprogress.js” file:

$(function(){
    $("input#uploadedFile").bind('change', null, function(){check_file_type()});
});

function getProgress(uploadKey){
    $.post(url+"match/json_get_uploadprogress",{"upload_id":uploadKey},
    function(data)
    {
        if(!data) return;
        var result = data.result;
        var percentage = 0;
        percentage = data.percentage;
        if (percentage > 0) {
            $("#progressBar").html("<h3>Upload progress...</h3><div class='progressGrey'>"+result+"<div class='progressRed' style='width:"+percentage+"%'>"+result+"</div></div>");
        } else {
            $("#progressBar").html("<h3>Upload progress...</h3><div class='progressGrey'>"+result+"</div>");
        }
        if (percentage < 100) {
            var timeoutID = window.setTimeout("getProgress(theUploadKey)", 1000); //Se consulta cada 1 segundo
        }
        if(percentage >= 100)
        {
            $("iframe#uploadIframe").css('display','block');
            var body = $("iframe#uploadIframe").contents().find("body");
            //Remaquetamos el body del iframe por culpa de IE
            body.css('border','0px none');
            body.css('background-color',$("body").css('background-color'));
            body.css('text-align','center');
        }
    }, "json");
}

function startProgress(uploadKey) {
    theUploadKey = uploadKey;
    $("#progressBar").css('display','block');
    $("#progressBar").html("<h3>Upload progress...</h3><div class='progressGrey'> </div>");
    $("form#uploadForm").css('display','none');
    getProgress(uploadKey);
    return null;
}

With this we have working our progress bar for file uploadings.

2 thoughts on “Upload progress bar in CodeIgniter without Flash

  • the variable $upload_id where comes from, where is generated, I have error en view because of that.

    PD: sorry for bad english

    • I have updated the code in the post. You can create it either in the controller that generates the view of the form (and pass it to the view) or you can create it directly in the view. This time I did it directly in the view for simplicity.

Leave a Reply

Your email address will not be published. Required fields are marked *