Sequentially download multiple files (with jQuery)

Unfortunately the images to this post were lost during the migration from Blogengine.Net to WordPress

On my previous job, I was working together with a colleague on a file transfer application which lived in the browser. We were up -and downloading multiple files at the same time and needed a rich user experience. To ensure the best experience when uploading we turned to swfupload, it’s free and I highly recommend it. But download was another story. The user experience for downloading files is actually something for which we entirely depend on the browser. It’s not that there is a lot of choice. You click a link to a file which the browser can’t open then you get a save dialog box and the browser shows you a progress bar. All that is out of the hands of the developer you just put the link there or stream the file directly to the client and let the browser do his thing.

But we offered the user multiple files and he could determine which files he wanted to download. This is a simplified mockup of how the screen looked.

If a user wanted to download the files he could click the links one by one and then save them. But how to make the “Download selected files” button work. After some googling I found that there are numerous solutions for upload but didn’t find any decent solution for a ‘richer’ download experience. And after thinking it through it makes sense, because downloading requires access to the hard disk of the user. This would be a serious risk to allow javascript to write to a certain folder on disk.

So we were stuck, some suggested of zipping all the zips to one file but we didn’t want to burden the end user with the extra effort of unzipping. I got a hint on a forum post (can’t remember which one sorry for that) that if you pointed the source attribute of an iframe to the file the browser would offer it as a download. So the first attempt to to this was with the following HTML code:

<html xmlns="http://www.w3.org/1999/xhtml">
	<head>
		<title></title>
		<script src="j/jquery.js" type="text/javascript"></script>
		<script src="j/index.js" type="text/javascript"></script>
	</head>
	<body>
		<h1>Download files</h1>
		<iframe src="" id="download-iframe" style="display:none;" />
	</body>
</html>
And the following JavaScript was executed when the document was loaded.

 $(function()
{
	$('#download-iframe').attr('src', 'file1.zip');
	$('#download-iframe').attr('src', 'file2.zip');
	$('#download-iframe').attr('src', 'file3.zip');
});

You can see I’m using some jQuery magic. If you haven’t used it yet, stop reading and go check it out. Did I already mention I’m a fan?

But there is a problem with the code. Only the last file is offered as download. I guessed, I’m not 100 % sure and please correct me if I’m wrong, that the problem was that the statements were executed directly after each other. So I used a little timeout to see if that was the solution to our problem, like so:

$(function()
{
	var fileIndex = 0;
	var fileArray = [ 'file1.zip', 'file2.zip', 'file3.zip' ];

	$('#download-iframe').attr('src', fileArray[fileIndex]);
	fileIndex++;

	var interval = setInterval(function()
	{
		if(fileIndex < fileArray.length)
		{
			$('#download-iframe').attr('src', fileArray[fileIndex]);
			fileIndex++;
		}
		else
		{
			clearInterval(interval);
		}
	},
	100);
});

As you can see the code is not that much longer, I now have an array of the filenames (could be extracted from a list of checkboxes) and offer the first file as download. Then I set an interval that fires every 100 milliseconds which offers the next file as a download. Once all files have been handled, the interval is cleared and we’re done.

Now the user gets prompted with 3 download dialog boxes. I know, it sucks that he still has to click three times to save each download individually. But if the user sets a default download location the files are automatically saved to that location and he doesn’t have to click for each file that gets downloaded. It’s certainly not perfect and from a user experience standpoint it still kinda sucks … but it works. If you have another idea of solving this problem, please feel free to elaborate in the comments.