Asynchronously upload file using PHP and HTML5

Published on: March 30, 2014 Written by: Thokozani Mhlongo

Today we are going to be uploading files through AJAX and using a bit of HTML5. To start of, we are going to configure our PHP ini file to accept large files. This is so that we can see the progress in our uploader when we're testing it.

Configuring PHP ini file

  1. Locate your php ini file in your server folder. If its XAMPP then your file might be in a path such as c:\xampp\php\php.ini and open it using Notepad.
  2. Now press CTRL + F and type in post_max_size and change the value to a higher one. This changes the amount of data you can send via a form and since we're dealing with files we might want to crank that number up a bit more.
  3. Press CTRL + F again and type in upload_max_filesize and also change that value to a higher one. This is the amount of data that a server can upload of a single file
  4. Save your file and restart your apache server

CSS Styling

body {
    padding: 10%;
    font-family: Arial;
    font-size: 12pt;
}

label {
    font-weight: bold;
    font-size: 16pt;
    color: #2ec3cc;
}

form {
    border: 1px dotted #ddd;
    padding: 2%;
}

input[type="file"] {
    border: 1px solid #ccc;
    border-radius: 2px;
    padding: 3px;
    width: 80%;
}

input[type="submit"] {
    background: #a1cc2e;
    color: #fff;
    padding: 1% 3%;
    border: 4px solid #81a520;
    border-radius: 60px;
    font-weight: bold;
}
input[type="submit"]:active {
    background: #b9e14f;
}

.progress {
    padding: 2% 0%;
}

.percentage {
    font-size: 10pt;
}

.percentage span {
    background: #0080C0;
    padding: 3px 10px;
    color: #fff;
    border-radius: 20px;
}

.percentage span.success {
    background: green;
}


This stylesheet is just for visuals on this demo only. You can skip this and add your own styles if you want

Our HTML markup

<!DOCTYPE html>
<html>
     .....
     <script type="text/javascript" src="jquery-1.8.2.min.js"></script>
     .....
<body>
    <form action="" method="post" enctype="multipart/form-data">
        <label for="files">Upload Files:</label><br/><br/>
        <input type="file" id="files" name="files[]" multiple="multiple" /><br/><br/>
        <input type="submit" id="upload" name="upload" value="Upload" /><br/>

        <!-- Progress will be shown here -->
        <div class="progress"></div>
    </form>
</body>
</html>

The markup seems straight-forward and display a form, an input file type element, and a button. Make sure to include enctype to mutlipart/fom-data in order to allow file uploads.

Server-side uploading using PHP

<?php

if(!empty($_FILES)) {
    $files = $_FILES['files'];
    
    for($i = 0; $i < count($files); $i++) {
        move_uploaded_file($files['tmp_name'], $files['name']); //Upload the file
    }
}

?>

This script is quite simple enough and its named upload.php. We use move_uploaded_file() to get the file that was sent via HTTP POST and save it in the same directory as where this script is.

Uploading using AJAX and HTML5

Now let's start adding some javascript code that does the magic. We can achieve this with the help of two APIs that HTML5 introduced and they are:

  1. FormData - This inteferface behaves just like a form by providing a way to have key/value pairs that can be sent via XMLHttpRequest. You can read more about browser compatibility here.
  2. File - This API contains properties about a file such as size, name, path, etc.
$(document).ready(function() {
        
    $('#upload').click(function(e) {
        e.preventDefault(); //Prevent a page load
                    
        var uploader = document.getElementById('files');
                    
        for(var i = 0; i < uploader.files.length; i++) {
            var data = new FormData(); //New with HTML5

            data.append('files' , uploader.files[i]); //Appending the File object
                        
            upload(uploader.files[i], i, data);
        }
    });
});
            
function upload(file, i, data) {
    var xhr = new XMLHttpRequest();
    
    //Upload progress listener                
    xhr.upload.addEventListener('progress', function(e) {
        if(e.lengthComputable) {
            var percentage = Math.ceil( ((e.loaded / e.total) * 100) );
            $('#' + i + ' span').text(percentage + '%'); //Updating the percentage <span> at i
        }
    });

    xhr.addEventListener('readystatechange', function(e) {
        if(this.readyState == 4) {
            if(this.status == 200) {
                $('#' + i + ' span').text('Upload 100% successful');
                $('#' + i + ' span').addClass('success');
            }
        }
    });
                        
    $('.progress').append('<p id="' + i + '" class="percentage">' + file.name + ' <span>0%</span></p>');

    xhr.open('POST', 'upload.php');
    xhr.setRequestHeader('Cache-control', 'no-cache');
    xhr.send(data);
}

In this piece of code we prevent the form from submitting, loop through each file, and use our FormData object to upload each of those files. The function upload() declares an XMLHttpRequest and add a couple of event listeners (one on the upload handler xhr.upload, and the other on the request when we get a response from the server).

It's worth noting that during the upload progress listener we have to first check if the length (e.lengthComputable) of the file is computable from the event itself because some browsers cannot.

Summary

We've just build our asynchronous file uploader by taking advantage of HTML5. Its best practice to implement a fallback just in case the client browser doesn't support HTML5. Do not forget to configure PHP to accept big files too, otherwise this might not work.

Comments