HTML5 Drag and Drop Multiple File Upload

Blog Projects
html5 javascript php

Previously I experimented with drag and drop file upload with Google Gears. Recently FireFox 3.6 (codenamed Namoroka) was the first to implement File API. It enables JavaScript to interact with local files.

Correction: Ionut G. Stan pointed out that File API was actually available already in FireFox 3.0. What Namoroka introduced is the drag and drop interface for the files. Sorry for the confusion.

Here is how you can implement drag and drop multiple file upload with native JavaScript. No plugins needed. Just plain old new HTML5. Again there is a working demo. You will need FireFox 3.6 to test it. Full source code can be at GitHub.

PHP Upload Script

PHP script for receiving the files is the same as last time. It accesses uploaded files using $_FILES superglobal. For this demo we assume users upload only image files.

<?php

$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

$upload_dir  = './tmp/';
$num_files = count($_FILES['user_file']['name']);

for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . basename($_FILES['user_file']['name'][$i]);

    if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['user_file']['name'][$i])) {
        print "I asked for an image...";
    } else {
        if (is_uploaded_file($_FILES['user_file']['tmp_name'][$i])) {
            if (move_uploaded_file($_FILES['user_file']['tmp_name'][$i], 
				$upload_file)) {
                /* Great success... */
            } else {
                print $error_message[$_FILES['user_file']['error'][$i]];
            }
        } else {
            print $error_message[$_FILES['user_file']['error'][$i]];
        }    
    }
}
?>

Setup Drop Target

Also JavaScript code is almost identical with the Google Gears version. Since only FireFox is supported we can leave Mozilla and IE initialization out. We only need to attach drop event to div with id #dropzone. Event will trigger function called upload().

$(function() {
        
    /* Cannot use $.bind() since jQuery does not normalize native events. */
    $('#dropzone')
        .get(0)
        .addEventListener('drop', upload, false);

    function upload(event) { 
		/* Uploading will here. */
	}

}

Upload Files With Native JavaScript

JavaScript is used to build RFC2388 string. This string is POST:ed using XMLHttpRequest to our PHP script. PHP will see the request as if it was POST:ed from normal multipart/form-data form.

There are three differences to Gears version:

function upload(event) {
    
    var data = event.dataTransfer;

    var boundary = '------multipartformboundary' + (new Date).getTime();
    var dashdash = '--';
    var crlf     = '\r\n';

    /* Build RFC2388 string. */
    var builder = '';

    builder += dashdash;
    builder += boundary;
    builder += crlf;
    
    var xhr = new XMLHttpRequest();
    
    /* For each dropped file. */
    for (var i = 0; i < data.files.length; i++) {
        var file = data.files[i];

        /* Generate headers. */            
        builder += 'Content-Disposition: form-data; name="user_file[]"';
        if (file.fileName) {
          builder += '; filename="' + file.fileName + '"';
        }
        builder += crlf;

        builder += 'Content-Type: application/octet-stream';
        builder += crlf;
        builder += crlf; 

        /* Append binary data. */
        builder += file.getAsBinary();
        builder += crlf;

        /* Write boundary. */
        builder += dashdash;
        builder += boundary;
        builder += crlf;
    }
    
    /* Mark end of the request. */
    builder += dashdash;
    builder += boundary;
    builder += dashdash;
    builder += crlf;

    xhr.open("POST", "upload.php", true);
    xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' 
        + boundary);
    xhr.sendAsBinary(builder);        
    
    xhr.onload = function(event) { 
        /* If we got an error display it. */
        if (xhr.responseText) {
            alert(xhr.responseText);
        }
        $("#dropzone").load("list.php?random=" +  (new Date).getTime());
    };
    
    /* Prevent FireFox opening the dragged file. */
    event.stopPropagation();
    
}

And there you have it. Native drag and drop multiple file upload.

More Reading

Ryan from CSS Ninja has similar article but with progress meter and thumbnail image. PPK got frustrated with HTML5 drag and drop. Leslie Michael has nice article about HTML5 drag and drop. Last but not least Ionut G. Stan helped me understand how to build RFC2388 strings.

Oh and almost forgot. If you need FireFox 3.6 check the nightlies folder.



When asking a question include an URL to example page where the problem occurs. Even better is to make a Fiddle which demonstrates the problem. If you have longer code examples please use pastie.org.
CATEGORIES
Built using the awesome Flat UI Pro framework by Designmodo.

Mika Tuupola is a Member of the Leanpub Affiliate Program.

© 2014 Mika Tuupola.