Wednesday 10 July 2013

HTML5 Drag and Drop Multiple File Upload

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:
  • RFC2388 string is built using JavaScript string variable instead of Gears BlobBuilder API.
  • Data is POST:ed using native XMLHttpRequest instead of Gears HttpRequest API.
  • Information on dropped files are accessed using event.dataTransfer variable instead of desktop.getDragData()function call.
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.

No comments:

Post a Comment