555-html5-file-drag-drop

How to Asynchronously Upload Files Using HTML5 and Ajax

By | | HTML5 | JavaScript

9

In my previous posts, we discovered How to Use HTML5 File Drag & Drop, and Open Files Using HTML5 and JavaScript. Now we have a valid set of files, it possible to upload each one to the server. The process occurs asynchronously in the background so the user can complete other on-page tasks while it occurs.

The HTML

Let’s examine our HTML form again:


<form id="upload" action="upload.php" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>HTML File Upload</legend>
<input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" />
<div>
	<label for="fileselect">Files to upload:</label>
	<input type="file" id="fileselect" name="fileselect[]" multiple="multiple" />
	<div id="filedrag">or drop files here</div>
</div>
<div id="submitbutton">
	<button type="submit">Upload Files</button>
</div>
</fieldset>
</form>

We’ll be uploading files to a PHP page, upload.php. The page will handle both the Ajax upload requests and standard form POSTs when the user clicks “Upload Files”.

Our JavaScript will ensure that only JPG images are uploaded which are smaller than 300,000 bytes — the value specified in MAX_FILE_SIZE.

The JavaScript

First, we require an additional line within our FileSelectHandler() function which is called when one or more files is chosen or dropped. Within our File loop, we’ll call an additional function — UploadFile():


// file selection
function FileSelectHandler(e) {
	// cancel event and hover styling
	FileDragHover(e);
	// fetch FileList object
	var files = e.target.files || e.dataTransfer.files;
	// process all File objects
	for (var i = 0, f; f = files[i]; i++) {
		ParseFile(f);
		UploadFile(f);
	}
}

File uploading requires the XMLHttpRequest2 object which is currently available in Firefox and Chrome. Before we make the Ajax call, we ensure an .upload() method is available and that we have a JPG with a file size less than the MAX_FILE_SIZE form value:


// upload JPEG files
function UploadFile(file) {
	var xhr = new XMLHttpRequest();
	if (xhr.upload && file.type == "image/jpeg" && file.size <= $id("MAX_FILE_SIZE").value) {

The XMLHttpRequest .open() method is set to POST data to upload.php, the action attribute of our upload form. In addition, we set an HTTP header to the file’s name and pass the File object to the .send() method:


		// start upload
		xhr.open("POST", $id("upload").action, true);
		xhr.setRequestHeader("X_FILENAME", file.name);
		xhr.send(file);
	}
}

The PHP

Our PHP file, upload.php, now checks for the X_FILENAME HTTP header to differentiate between Ajax requests and standard form POSTs:


<?php
$fn = (isset($_SERVER['HTTP_X_FILENAME']) ? $_SERVER['HTTP_X_FILENAME'] : false);

If a filename has been set, PHP can retrieve the posted data and output it to a new file in an ‘uploads’ folder. Amazingly, this can be achieved in a single line of code:


if ($fn) {
	// AJAX call
	file_put_contents(
		'uploads/' . $fn,
		file_get_contents('php://input')
	);
	echo "$fn uploaded";
	exit();
}

Standard HTML multipart/form-data posts can be handled using the usual PHP $_FILE functions:


else {
	// form submit
	$files = $_FILES['fileselect'];
	foreach ($files['error'] as $id => $err) {
		if ($err == UPLOAD_ERR_OK) {
			$fn = $files['name'][$id];
			move_uploaded_file(
				$files['tmp_name'][$id],
				'uploads/' . $fn
			);
			echo "<p>File $fn uploaded.</p>";
		}
	}
}

You can view the demonstration page, however, please note it is hosted on a server without PHP support and the upload will not occur. Therefore, please download the files to examine the code and install it on your own PHP server.

The code above will work, but the user won’t know whether a file upload has started, finished or failed. You need to read the final instalment in this series: How to Create File Upload Progress Bars in HTML5 and JavaScript

If you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like HTML5 & CSS3 For the Real World.

The Ultimate JavaScript Bundle: 2 books + 1 course

Buy now $39 Normally $117 - save 66%

Or get access to all SitePoint's Premium Content with a Learnable membership

Craig Buckler

Craig is a Director of OptimalWorks, a UK consultancy dedicated to building award-winning websites implementing standards, accessibility, SEO, and best-practice techniques.

More Posts - Website

{ 9 comments }

Anonymous October 18, 2011 at 8:23 am

Thanks for the tutorial Craig. I just have a general JS question about the code (I couldn’t formulate a Google query for this…) but why is there:
(function() {
at the top and
})();
at the bottom.

Is this a basic/general best practice? What are the benefits if so?

Thanks again!

Anonymous August 26, 2011 at 3:22 pm

Thank you I will give it a try.

Satya Prakash August 24, 2011 at 6:08 pm

very good example and code to download.

Anonymous August 24, 2011 at 4:08 pm

Hi Craig
Nice tutorial and it is very useful.
I just want to know how should I handle data (normal input fields) that should be sent with the files to the server. I want to add this functionality to a WordPress plugin I am developing Post Office.

OyoKooN August 25, 2011 at 1:02 am

I’d like to send POST data in parallel of the file too.
I’m trying to figure out how to do that as I’m working with Rails but I can’t find the solution.

I tried to put my params into a string, including the file but it don’t seems to work.

Craig Buckler August 25, 2011 at 12:18 pm

If you’re sending all the form data including the files, you don’t need to use XMLHttpRequest – simply do a normal POST.

If you want to use Ajax, you’ll need to use separate calls to upload each file and the form data separately.

Graham August 25, 2011 at 1:09 pm

I’m unsure of how this can work – if its a standard POST, surely there’s no way to pick up files obtained via FileList objects?

OyoKooN August 25, 2011 at 1:11 pm

I managed to do it.
Everything is in my comment, awaiting moderation.

Craig Buckler August 25, 2011 at 5:00 pm

Mmm, I’d have to check but it should be possible to set the files property of the file select box to dataTransfer.files or a list of File objects – see FileSelectHandler().

But perhaps OyoKoon has another solution…

Comments on this entry are closed.