HTML5 File Drag, Drop, Analyze, Read and Upload

Tweet

It’s been a busy week. We’ve discovered how the new HTML5 APIs can help us open, read and upload files which the user dragged and dropped into the browser window. This article summarizes the techniques and the current level of browser support.

HTML5 API Support

Your JavaScript code should check for the existence of the File, FileList and FileReader objects prior to attaching event handlers. At the time of writing, these are supported by the latest versions of Chrome, Firefox and Opera:


if (window.File && window.FileList && window.FileReader) { ... }

Although Opera supports these objects, they can only be used via a standard file input — not drag and drop. Therefore, a further check is required; I suggest using the XMLHttpRequest2 upload method, e.g.


var xhr = new XMLHttpRequest();
if (xhr.upload) {
	... attach drag and drop events ...
}

File Drag & Drop

All browsers (except those on the iPhone and iPad) support the file input type which displays the familiar “Browse” button. A “multiple” attribute has been introduced in HTML5 and we can attach a change event handler to the field:


document.getElementById("fileselect").addEventListener("change", FileSelectHandler, false);

Chrome and Firefox also allow users to drag one or more files on to a chosen element. You can attach event handlers including “dragover” and “dragleave” (for changing styles) and “drop” for detecting dropped files, e.g.


document.getElementById("filedrag").addEventListener("drop", FileSelectHandler, false);

Retrieving a FileList Object

The HTML5 FileList object is an array-like collection of File objects. File input fields return a FileList via a files property (event.target.files). Dropped files return a FileList object via the event’s dataTransfer.files property (event.dataTransfer.files).

We can therefore retrieve a FileList object using single event handler:


// cancel event default
e.preventDefault();

// fetch FileList object
var files = e.target.files || e.dataTransfer.files;

// process all File objects
for (var i = 0, file; file = files[i]; i++) {
	...
}

It’s important to cancel the default event. This prevents the browser attempting to display or handle a file when it’s dropped into the window.

Analyzing File Objects

FileList collections contain a number of File objects. Three useful File properties are provided:

  1. .name: the file name (it does not include path information)
  2. .type: the MIME type, e.g. image/jpeg, text/plain, etc.
  3. .size: the file size in bytes.

It’s possible to check a file type and size before further processing or uploads occur, e.g.


// process image files under 300,000 bytes
if (file.type.indexOf("image") == 0 && file.size < 300000) {
	...
}

For more information, refer to How to Open Dropped Files Using HTML5 and JavaScript.

Opening Files using FileReader

The HTML5 FileReader object allows you to open text or binary files in JavaScript. As you’d expect, the readAsText() method is used for retrieving text content, e.g.


if (file.type.indexOf("text") == 0) {
    var reader = new FileReader();
    reader.onload = function(e) {
		// get file content
		var text = e.target.result;
		...
    }
    reader.readAsText(file);
}

Similarly, the readAsDataURL() method retrieves binary image data as an encoded data URL which can be passed to an image src attribute or canvas element:


if (file.type.indexOf("image") == 0) {
    var reader = new FileReader();
    reader.onload = function(e) {
		document.getElementById("myimage").src = e.target.result;
    }
    reader.readAsDataURL(file);
}

For more information, refer to How to Open Dropped Files Using HTML5 and JavaScript.

Uploading Files using Ajax

Appropriate files can be uploaded to your server while the user remains on the page. It’s simply a matter of passing a File object to the send() method of XMLHttpRequest2:


var xhr = new XMLHttpRequest();
xhr.open("POST", "receivefile.php", true);
xhr.setRequestHeader("X_FILENAME", file.name);
xhr.send(file);

Note we’ve also sent the filename as an HTTP header. This is optional, but it allows us to recreate the file using its original name on the server using a language such as PHP:


file_put_contents(
	'uploads/' . $_SERVER['HTTP_X_FILENAME'],
	file_get_contents('php://input')
);

For more information, refer to How to Asynchronously Upload Files Using HTML5 and Ajax.

Creating Upload Progress Bars

We can also attach a “progress” event to XMLHttpRequest2 objects:


xhr.upload.addEventListener("progress", ProgressHandler);

The handler receives an event object with .loaded (the number of bytes transferred) and .total (the file size) properties. Therefore, the progress can be calculated and passed to an HTML5 progress tag or any other element, e.g.


function ProgressHandler(e) {
	var complete = Math.round(e.loaded / e.total * 100);
	console.log(complete + "% complete");
}

For more information, refer to How to Create Graphical File Upload Progress Bars in HTML5 and JavaScript.

I hope you enjoyed this series. File drag and drop is an important feature which can transform web application usability. HTML5 finally makes it easy.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.facebook.com/eric.stern Eric Stern

    Nice guide, but your PHP example on how to save the file at the end is a massive security vulnerability. I could very easily send a filename of ‘../../../../../etc/passwd’ and have all sorts of fun with your server (hopefully your server’s web user doesn’t have access to that file, but you get the idea). The example should at least do some basic filtering, e.g. basename(), so that attackers can’t upload or replace files at random.

    • http://twitter.com/craigbuckler Craig Buckler

      I agree but, like I stated in the first article, “my code shows the fundamental concepts.” In this case, a simple check for ‘/’ would thwart most security issues.

  • http://www.alfystudio.com Ahmad Alfy

    Believe it … some people are creepy …

    • http://profiles.google.com/oscarvillarreal14 Oscar Villarreal

      Creepy is the best description I’ve heard so far

  • http://pulse.yahoo.com/_6GG7CEFACSCBVP2LFIJ23KVSYQ Emma Pope

    i cant believe this!! me and my sister just got two i-pads for $42.77 each and a $50 amazon card for $9. the stores want to keep this a secret and they dont tell you.
    go here, pluscent.com

    • Anonymous

      Wow! That’s amazing. Can’t believe it has anything to do with HTML5… Oh wait, you didn’t even read the article obviously. What an idiot. Spend your time doing something productive.

  • http://www.joezimjs.com JoeZimJS

    This is really cool. I’ve been wondering if something like this was possible with straight JS and now I know you can (though only for certain people). I’ll have to talk about this a bit on my blog at http://joezimjs.com.

    Is this how Facebook does it, or do they use a plugin like Flash or Java? I’m assuming they use Flash, otherwise a lot of people would have to use a basic uploader.