PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Monitoring File Uploads using Ajax and PHP

Developing a JavaScript Monitoring Class

Now that all required PHP code for this article has been covered we can implement the required JavaScript code. Note that the code we develop here makes extensive use of the advancements provided by Prototype. You can download the prototype.js file from http://prototypejs.org. Version 1.6.0 is used in this article. Note that this article doesn't cover extensively how Prototype works, however I will try to keep the explanations as simple as possible.

The code in this section will be broken up once again by method, while the complete file can be downloaded from the article file list.

Initializing the FileUploader JavaScript Class

We begin the class by creating several variables that are used in the class. Firstly, we create a variable called ID_KEY to hold the name of the element required for APC upload monitoring. Once again this is just to keep the code clean and to avoid using magic values. We also define the URL where the status of an uploaded file can be retrieved from in statusUrl variable.

Please read the extensive comments to understand how the code works. The important points regarding Prototype that the comments do not describe include:

  • Class.create() is how classes are created using Prototype. More information can be found at http://prototypejs.org/api/class/create.
  • When creating a class with Prototype, the constructor function is called initialize().
  • Events can be observed with Prototype using the observe() method. The first argument is the name of the event without the 'on' (so onmouseover would be mouseover). The second argument is the handler function. This code binds the handler function to the instance of FileUploader. More information on function binding with Prototype can be found at http://prototypejs.org/api/function.
  • Since Prototype 1.6.0, new DOM elements can easily be created using the Element class. More information can be found at http://prototypejs.org/api/element.
Listing 14 Initializing the FileUploader JavaScript class (listing-14.js)
var FileUploader = Class.create({
 
    ID_KEY         : 'APC_UPLOAD_PROGRESS',
    statusUrl      : 'status.php',
    pollDelay      : 0.5,
 
    form           : null, // HTML form element
    status         : null, // element where the upload status is displayed
    statusTemplate : null, // Prototype Template object
    idElement      : null, // element that holds the APC upload ID
    iframe         : null, // iframe we create that form will submit to
 
    initialize : function(form, status)
    {
        // initialize the form and observe the submit element
        this.form = $(form);
        this.form.observe('submit', this._onFormSubmit.bindAsEventListener(this));
 
        // create a hidden iframe
        this.iframe = new Element('iframe', { name : '_upload_frame' }).hide();
 
        // make the form submit to the hidden iframe
        this.form.appendChild(this.iframe);
        this.form.target = this.iframe.name;
 
        // initialize the APC ID element so we can write a value to it later
        this.idElement = this.form.getInputs(null, this.ID_KEY)[0];
 
        // initialize the status container
        this.status = $(status);
        
        // create a template based on the HTML inside the status container
        this.statusTemplate = new Template(this.status.innerHTML);
        
        // clear the status template
        this.status.update();
    },
 
    // ... other code
});
Note: Because each method is an element of a JavaScript object, they must be separated by commas. Therefore, each method must end in a comma after the closing brace, except for the final method. You will be able to see this clearly in the full class file.

Utility Methods

Next we will define two utility methods that help with our implementation. The first method is used to generate an ID for the upload file. This value is used when calling the upload.php file. We will generate this value based on the current date and time, thereby allowing a user to upload a second file after their first one has completed.

The other method we define is used to “pause” our code as required. Since we are going to periodically poll the status script we need a way to time this polling.

Listing 15 Utility methods to help with monitoring upload status (listing-15.js)
var FileUploader = Class.create({
 
    // ... other code
 
    generateId : function()
    {
        var now = new Date();
        return now.getTime();
    },
 
    delay : function(seconds)
    {
        var ms   = seconds * 1000;
        var then = new Date().getTime();
        var now  = then;
 
        while ((now - then) < ms)
            now = new Date().getTime();
    },
 
    // ... other code
});

Handling the Upload Form Submission

In the initialize() method, the code is instructed to call the _onFormSubmit() method when the user submits the upload form. We will now implement this method. The goal with this method is to firstly generate a unique ID for this upload and write it to the form before it is submitted, then to continue to monitor the upload until it has completed.

We will use the generateId() method just created and write the returned value to the idElement variable we created in initialize(). The _onFormSubmit() method concludes by calling _monitorUpload(). We will define that method shortly.

Listing 16 Handling the form submission event (listing-16.js)
var FileUploader = Class.create({
 
    // ... other code
 
    _onFormSubmit : function(e)
    {
        var id = this.generateId();
 
        this.idElement.value = id;
        this._monitorUpload(id);
    },
 
    // ... other code
});

Monitoring the File Upload

We can now implement the actual upload monitoring Ajax code. As we just saw, this is performed in a method called _monitorUpload(). When this method is called, an Ajax request is initiated. The status.php script created earlier in this article is requested using the ID passed to _monitorUpload(). This results in the JSON data for the given ID being returned.

An Ajax request is initiated in Prototype by instantiating the Ajax.Request class. The first argument is the URL being requested and the second argument is the required arguments. By default the request method is post, and we define the post data by specifying the parameters element in the options object.

The other option specified is the onSuccess argument. The value for this setting is the function that is called once the Ajax request successfully completes. Once again we use function binding, which you can read more about at the Prototype web site (http://prototypejs.org/api/function).

Listing 17 Monitoring the file upload progress (listing-17.js)
var FileUploader = Class.create({
 
    // ... other code
 
    _monitorUpload : function(id)
    {
        var options = {
            parameters : 'id=' + id,
            onSuccess  : this._onMonitorSuccess.bind(this)
        };
 
        new Ajax.Request(this.statusUrl, options);
    },
 
    _onMonitorSuccess : function(transport)
    {
        var json = transport.responseJSON;
 
        this.status.show();
        this.status.update(this.statusTemplate.evaluate(json));
 
        if (!json.finished) {
            this.delay(this.pollDelay);
            this._monitorUpload(json.id);
        }
    }
});

Once the Ajax request initiated in _monitorUpload() successfully completes, the _onMonitorSuccess() method is called. The Ajax.Request class automatically passes the request data as the first argument (which we have called transport).

From Prototype 1.6.0 we can access returned JSON data using the responseJSON element.

Note: In previous versions of Prototype you would need to use a line such as var json = transport.responseText.evalJSON(true) instead.

Once we have access to this JSON data we use it to update the status container. We firstly use the Template object to generate HTML with the values substituted in, then call update() to insert this HTML into the status container.

The code concludes by checking whether or not the upload has finished. If it has not yet finished, it pauses briefly then calls _monitorUpload() again. This will continue to happen until the upload completes.

The full code listing for this class can be downloading from the article file list.

In This Article


Additional Files