Plupload 2.1, Chrome and folder support

I'm currently developing an intranet application. Intranet applications are great! You just get your client to use a suitable browser and you can basically use whatever front end technology out there.

The application

My client employs a couple of photographers who, at the end of the day, will upload their day's work via the new intranet application. To make this as easy as possible I built a custom widget for Plupload that introduces a four step process:

  1. Drag and drop as many files and folders as you want
  2. Select which job each file and folder belongs to
  3. Flag multiple exposures and photos that need editing
  4. Start the upload and wait for it to complete

I accomplished this using a combination of Bootstrap Wizard and Plupload. I was amazed how simple it was:

  • Wizard step 1: big drop-enabled div
  • Wizard step 2: list of folders with a select box next to each
  • Wizard step 3: Plupload's amazing thumbnails with some bespoke flagging functionality
  • Wizard step 4: Plupload's upload script with a nicely animated progress bar

The problem

Only pitfall was the folder mapping in step 2. I knew I could get the dropped items's folder structure using Chrome's webkitGetAsEntry, but Plupload doesn't support that (yet). Like quite a few other developers using Plupload 2 for uploading folders, I was desperate to get a version that would support relative paths. I tried hacking the core script, but it doesn't seem to be possible by amending just a few lines.

The solution

So I decided to keep things simple until Plupload 2.1.3 supposedly will be shipped with the needed feature. I just bind another handler to the drop event, build an object representing the folder structure and send it along when uploading.

<div id="drop-target">
	<input type="file" id="files" />
<button id="uploadfiles">Upload!</button>

<script src="//"></script>
var uploader, traverseFileTree, map = {};

// replace by your plupload setup, this is just an example
uploader = new plupload.Uploader({
	runtimes : 'html5',
	container: 'drop-target',
	drop_element: 'drop-target',
	browse_button : 'files',
	url : '',
	init: {
		PostInit: function() {
			document.getElementById('uploadfiles').onclick = function() {
				return false;
		BeforeUpload: function (up, file) {
			// send relativePath along
			if(map[] !== undefined) {
				up.setOption('multipart_params', {
					relativePath: map[].shift()

// all relative paths are built here
traverseFileTree = function (item, path) {
	var dirReader = null;
	path = path || '';
	if (item.isFile) {
		item.file(function(file) {
			// careful here, could be several files of the same name
			// we assume files will be in the same order here than in plupload
			if(map[] === undefined) {
				map[] = [];
	} else if (item.isDirectory) {
		dirReader = item.createReader();
		dirReader.readEntries(function (entries) {
			var n = 0;
			for (n = 0; n < entries.length; n++) {
				traverseFileTree(entries[n], path + + "/");

// bind another handler to the drop event to build an object representing the folder structure
document.getElementById('drop-target').addEventListener('drop', function(e) {
	var items = e.dataTransfer.items, n, item;
	for(n = 0; n < items.length; n++) {
		item = items[n].webkitGetAsEntry();
		if(item) {
}, false);