HTML5 Image, Zip, and Directory Upload to Imgur
This article and demo includes implementation of HTML5 File API, FormReader, FormData, Web Workers, Drag and Drop, and Cross-Domain XHR via Imgur API
Recently, I was impressed with the simple and clean implementation of Paul Rouget’s imgur uploader. You get drag and drop, changing body classes to indicate upload state, and your file gets pushed to Imgur via Cross-Domain XHR. Pretty cool and done in a couple lines of code. I have also recently stumbled upon zip.js and it made me want to enhance Paul Rouget’s example with the ability to upload an archive of images. And, in that spirit, let’s also allow multiple files, any images found on the internet, multiple zip archives, and a directory or multiple directories of images.
So, let’s start with the HTML. We want to support drag and drop into a div element as well as manual selection of files, directories or zip files. So we have a 3 inputs, one that allows multiple files, one that only accepts “application/zip” for zip archives, and one that supports selecting directories via webkitdirectory attribute. The webkitdirectory attribute is currently only supported by Chrome but all other browsers can drag folder(s) instead of selecting with input.
To change the “choose file” wording on the inputs, I hid them with css (visibility: collapse;width: 0px;) and created buttons with more descriptive text. I added JavaScript event handlers to fire on them when buttons are clicked. There are two div elements to capture results from Imgur (uploaded images) and any errors from the xhr request (or if someone is trying to select non-images). Finally, I added a forth input element and a submit button to capture a URL of an image hosted on another website. All of this gets cleaned up in the final demo but here is this part of the code:
Next, we have the drag and drop. When files, folders, or zip files are dropped into the div element, the ondrop function fires and iterates through the dropped items. I used simple regex to check if a file is a zip archive or an image type (Imgur obviously only allows uploading images but you could extend this example to upload any content to your web server and would not need these conditionals). If we are working with image files, they are sent over to the upload function. If we are working with one directory or multiple directories, we send them to the traverseFileTree function. This function uses the HTML5 File API which has a createReader method that creates a new DirectoryReader to read files from a given directory. So, I iterate through all the directories and send any image files to the upload function.
If a zip file is found, it gets passed to the unzip function that uses ZipJS to retrieve the files inside the archive (I added the zip.js file in the HEAD and it uses HTML5 Web Workers to uncompress the archives). There are other scripts which allow you to unzip files with JavaScript but I liked ZipJS approach and API. It actually allows you to decide on using a Blob or FileSystem for temporary storage but for simplicity, I have decided on using a Blob. Inside the getData() function, I setup a FileReader that reads the contents of the image file and passes the base64 image data to the upload function.
The upload function is also really simple. I use the HTML5 FormData and append the image file or base64 data to the image parameter. Then we make a Cross-Domain XHR request to Imgur and onload we check for the status response. If the POST came back with a 400, we parse the JSON and print the error message to the error element via innerHTML. Otherwise, it’s a success and I parse the JSON response to retrieve the thumbnail and image url so that I can build a link and image object and append it to the output element. Finally, we change the body class name from uploading to uploaded when each file is successfully sent over to Imgur. Here is that part of the JavaScript code:
Looking at all the code, it’s still small, but I would probably write it in half as many lines if I used jQuery. But, since I write a ton with jQuery already, I wanted to practice some pure JavaScript. Here is the full Imgur Upload demo. So, what do you think?
On a final note, I wanted to share some links to other interesting HTML5 upload implementations:
- html5uploader – Drag and drop files to browser using HTML5, PHP and JS
- HTML5 Multifile Upload – supports progress bar
- Upload Directory – good example with HTML5 Storage
- HTML5 FileSystem Playground – cool demo for filer.js, a well-tested wrapper library for the HTML5 Filesystem API
- JavaScript Load Image Demo – loads images provided as File or Blob objects (or via URL) and returns an HTML image or canvas element
- Great article on the File API
- File API and making animated GIFs
Update: It looks like we will see an Archive API (ability to read the content of an archive file through DOM File objects) from browsers in the future, as demonstrated by the work at Mozilla. Also, I have found a great real-world example by Raymond Camden on using the HTML5 FileSystem API. Andrew Dodson also did a good write-up on the File API and Shiv Kumar has a great post about using the Progress Event with Html5 File Upload.