HTML 5 File API Features
I think the File API is, arguably, one of the most sought after features of HTML 5. The ability to handle files, especially images, on the client side is incredibly appealing. Not only will you be able to have more advanced uploaders with progress bars (with the progress event and canvas) without using Flash, but you could resize images client side and then upload them saving you the server resources needed to run GD or ImageMagick. Cheeky? Yes. Entirely out of the question? I don’t think so.
Basic File API Usage
Let’s take a look at how you can use the File API.
1 |
<input type="file" multiple="true" onchange="filesProcess(this.files)" /> |
A very simple way to use the File API. You may have noticed the multiple="true"
this is also a feature of HTML 5. The ability to upload multiple items using a simple input element.
You may also be wondering what filesProcess(this.files)
is all about. Well that’s where we send away the data provided by the File API to said function to be ‘processed’. It’s worth noting that this.files
is passed as an array of objects. So your filesProcess()
function would likely contain:
1 2 3 4 |
for( var i = 0; i < files.length; i++) { var file = files[i]; //... } |
To loop through each file as you process it.
Another awesome feature is the ability to find some vital info about the file before you ever consider uploading it to the server. Things like it’s name, size, and MIME type.
More Complex File API Example
Let’s say, for a completely pointless reason, you want to alert the information (mentioned earlier) back to the user. Let’s take a look at how that would be achieved.
1 2 3 4 5 6 |
function filesProcess(files) { for( var i = 0; i < files.length; i++) { file = files[i]; alert("name: "+file.name+" | "+file.size+" | "+file.type); } } |
I passed an image called ‘7fdf972674f1.jpg’ which was approximately 3.03MB in size to my test and here is what I got back:
name: 7fdf972674f1.jpg | 3183176 | image/jpeg
It’s not particularly useful, but think about what you could do now that you actually have access to the file on the client side. You could dynamically create thumbnails, without ever touching the server, by using canvas (we’ll look at that soon).
Actually Uploading Files With The File API
Okay, to stop this tutorial being completely pointless let’s look at how to upload files asynchronously. This may change depending upon changes to the File API as HTML 5 is developed, however at time of writing this works quite well for me.
Assuming you have used the file input element from before let’s take a look at the Javascript code used to upload the images selected. Firs our filesProcess()
function to process the files & pass them to the async uploading function.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function filesProcess(files) { for (var i = 0; i < files.length; i++) { var file = files[i]; var typeFilter = /image.*/; if(!file.type.match(typeFilter)) { alert('This file isn\'t an image. Skipping...'); continue; } new uploadImg(file); } } |
Fairly simple. We loop through each file selected, check the file type (more on that in the quick tip later), and then we create a new uploader by handing the file to uploadImg()
.
Now we can look at the code used to asynchronously upload the file to the server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
function uploadImg(file) { var fileName = file.name; //Grab the file name var fileSize = file.size; //Grab the file size var fileType = file.type; //Grab the file type var reader = new FileReader(); //Create FileReader object to read the image data reader.readAsBinaryString(file); //Start reading the image out as binary data reader.onload = function() { //Execute this when the image is successfully read var boundary = "fileboundary"; //Boundary name var uri = "pathtoupload.php"; //Path to script for handling the file sent var xhr = new XMLHttpRequest(); //Create the object to handle async requests xhr.open("POST", uri, true); //Open a request to the web address set //Next two lines set headers to fool receiving server into thinking they were sent via form xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary); xhr.setRequestHeader("Content-Length", fileSize); //Set up the body of the POST data includes the name & image data. var body = "--" + boundary + "\r\n"; body += "Content-Disposition: form-data; name='fileId'; filename='" + fileName + "'\r\n"; body += "Content-Type: " + fileType + "\r\n\r\n"; body += reader.result + "\r\n"; body += "--" + boundary + "--"; //Use sendAsBinary to send binary data. If you are sending text just use send. xhr.sendAsBinary(body); } return true; } |
I must admit that this was tricky & I referenced the MDC (Mozilla Developer Center) a lot. Their code didn’t work for me so I’ve managed to cobble together a working version that doesn’t use the deprecated method of using the File
object to read file data. The comments in the code should be enough to explain what is going on, but I totally encourage you to ask any questions on anything you don’t understand.
One extra thing to note is that adding those headers fools your server side script into thinking the file was sent via a standard mutipart POST form. So unlike some Flash style uploaders the file data will be held in the normal $_FILES
superglobal, if you are using PHP. From there you can do extra checks on the MIME type, if needed, and save the file as normal using move_uploaded_file()
, again if you are using PHP.
If you are having trouble I recommend using Firebug to check the responses coming from the server side script etc.
Also one other note. At time of writing not all browsers support sendAsBinary()
on the XMLHttpRequest()
object. However as the object has now be accepted as a standard by the W3C I would hope other browsers will have similar functions available soon.
Quick Tip
I thought I’d add this quick tip. If you run a website which solely deals in images you don’t want to be handling files which aren’t images. The best way would be to use the accept
attribute which is used like this:
1 |
<input type="file" multiple onchange="filesProcess(this.files)" accept="image/*" /> |
However at the time of writing I am unaware of any browser that currently supports the feature. So until that is available there is another way. Always remember though that you should check file/MIME type again on the server side just in case.
1 2 3 4 5 6 7 8 9 |
function filesProcess(files) { for( var i = 0; i < files.length; i++) { file = files[i]; if(!file.type.match(/image.*/)) { alert('Files of this type may not be uploaded'); continue; //Jump forward to the next file... } } } |
Obviously it’s not the most elegant solution, but it works until the accept attribute is finally added to browsers.
What Is The Point?
Ahh yes. You may be asking what is the point when half of these features aren’t supported yet by all browsers? Well the answer is they are or will be very soon. The File API is currently partially (quite a large amount) finished in the latest versions of Gecko & Webkit browsers like Firefox 3.6+, and Chrome 6+. Safari 5.0.2 seems to have support for the File API too but seems to be unable to loop through more than one file, for me at least. If anyone knows why, I’d love to hear from you.
I guess the main reason for trying it is because we can. If we experiment while it’s in development maybe developers will listen to the suggestions, and improvements we give through their usage. Also learn about them now, then when they are finalized you already have a fair idea how to use them.
You may have also read recently that the W3C say HTML 5 is not ready for open deployment yet… In my opinion some features are & some aren’t. However there is no reason to stop trying to develop with them & even release some sites using HTML 5 features as long as you keep in mind you will need alternatives for browsers not able to use them.
I hope you’ve enjoyed this strange, but hopefully informative look at the HTML 5 File API and some of it’s features. If you have any suggestions, or you need some help let me know and I’ll get back to you. Also we will be looking at the File API some more a little later as well as Canvas and how to use it to create thumbnails from images handled by the File API.
9 Comments
madsenfr
Just to inform you that I made a page with a live demonstration of FileAPI base upon your article.
Have a look here http://w3c.html5.free.fr/news/fileAPI.php.
I also want to inform you that Safari 5.0.2 and now 5.0.3 support “multiple” selection and that Chrome in its beta and dev version support “accept” filter.
Paul Robinson
Awesome stuff. Thanks for sharing & thank you for the extra info. 🙂
Tony Arnold
I have to wonder where do you figure out what browsers support the new stuff?
Paul Robinson
Mainly via Twitter, or via the official blogs for Mozilla, Opera, IE, Chrome, and Safari. There are also lists on various websites.
madsenfr
Have a look here http://w3c.html5.free.fr/news/fileAPI.php
Dimitar Christoff
Cheers for that, needed a quick firefox-only ajax uploader and the MDC example failed where yours worked fine. Saved me a few hours for sure.
Paul Robinson
No worries. Glad it was helpful. 🙂
cheaken
What is FileReader() ? And where is definition of this function?
Paul Robinson
Hi,
FileReader()
is a object built into most modern browsers that support the FileAPI. It is defined inside your browser and doesn’t need to be defined by you.To make it compatible with older browsers you may want to use a HTML5 Javascript plugin such as Modernizer and use it to check the client’s browser has FileAPI support before trying to create a new
FileReader
.Hope that helps.