I finally had some time to read through my ever growing list must read items and play with some new software. While reading up on the new Firefox 3.6 i noticed it came with the new XMLHttpRequest [2] object based on the new file API. And according to the new specs. This would allow for easy file uploads. Now there’s been some examples [2] on the web already. But i just wanted to get my hands dirty.
The new XMLHttpRequest object makes is possible to send files in a few different formats. The most important being the binary format. The code for sending a request with XMLHttpRequest2 looks the same as the previous version. Except for sendAsBinary() in this case.
var xhr = new XMLHttpRequest();
fileUpload = xhr.upload,
fileUpload.onload = function() {
console.log("Sent!");
}
xhr.open("POST", "upload.php", true);
xhr.sendAsBinary(file.getAsBinary());
So let’s set things up for drag & drop. We need a div that will be the main drop point. And we need some event listeners to catch the drag * drop events. Let start by creating the drop zone. For this we use two simple divs. The outer div will listen for the drag & drop events. And the inner will catch the files.
Now let’s create our upload code.
var upload = {
setup : function() {},
uploadFiles : function() {event}
}
window.addEventListener("load", upload.setup, false);
The setup method will set all event listeners for drag & drop. And register the upload handler.
var container = document.getElementById('container');
var drop = document.getElementById('drop');
container.addEventListener("dragenter", function(event) {
drop.innerHTML = '';
event.stopPropagation();
event.preventDefault();
},
false
);
container.addEventListener("dragover", function(event) {
event.stopPropagation();
event.preventDefault();
},
false
);
container.addEventListener("drop", upload.uploadFiles, false);
As you could see above. the uploadFiles() method gets a event returned from the drag & drop action. This is where the new file APi comes in play. To get to the file property we access the dataTransfer object.
var files = event.dataTransfer.files;
The actual uploading is easy as cake.
for (var x = 0; x < files.length; x++) {
var file = files.item(x);
var xhr = new XMLHttpRequest();
fileUpload = xhr.upload,
fileUpload.onload = function() {
console.log("Sent!");
}
xhr.open("POST", "upload.php", true);
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.setRequestHeader("X-File-Name", file.fileName);
xhr.setRequestHeader("X-File-Size", file.fileSize);
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.sendAsBinary(file.getAsBinary());
}
That's it for the client side. There is however a small problem on the receiving side. When handling uploaded files in PHP we expect the $_FILES array to be populated. This is not the case when streaming files from the client to the server. To get the needed file information we set some headers on the client side X-File-Name and X-File-Size. And since the $_FILES are is empty. We need an other way to get the file contents. So we will use php://input streams for that.
The contents of upload.php look like this:
require_once('Streamer.php');
$ft = new File_Streamer();
$ft->setDestination('data/');
$ft->receive();
With setDestination() the destination path for the uploaded files is set. And recieve() listens for any incoming files. Most of the magic is done in the recieve() method. So here's the code.
public function receive()
{
if (!$this->isValid()) {
throw new Exception('No file uploaded!');
}
file_put_contents(
$this->_destination . $this->_fileName,
file_get_contents("php://input")
);
return true;
}
I am impressed! This promises a lot of good. And offers some interesting options. Let's hope all browsers implement this gem. I still have one issue though. I can't get this to work in firefox under linux. The drag & drop events do not seem to function properly with files being dragged from the desktop. anybody know why?
If you interested in the complete code. you can find it here
**Small update**
http://lenss.nl/2010/09/drag-drop-uploads-with-xmlhttprequest2-and-php-revised/



Thijs Lensselink is a PHP developer, consultant and all out open source enthusiast.
He has over 12+ years of experience in building and maintaining web applications mostly
on linux/Unix/BSD platforms. Besides a full time job he does freelance work with his ...
[...] Drag & drop Uploads with XMLHttpRequest2 and PHP at Web … Share and [...]
Drag & drop Uploads with XMLHttpRequest2 and PHP at Web … | Drakz upload Online Service
25 Jan 10 at 05:49
Did you manage to get your code work on the server side? It seems php://input is not available for \multipart/form-data\..
Michael
17 May 10 at 22:24
The code works fine. What browser are you testing this in?
Thijs Lensselink
18 May 10 at 08:27
How do i get the extension of the file ?
Sarath D R
16 Aug 10 at 04:47
I didn’t find an attribute yet for the file extension. But you could use something like this inside the XMLHTTPRequest2.
file.name.split(‘.’).pop()
Thijs Lensselink
16 Aug 10 at 08:28
it works OK with small files
however I cant get it working with files larger than 20Mb. Actually I got that in my logs :
[Thu Sep 16 16:09:02 2010] [error] [client 10.10.222.2] PHP Fatal error: Allowed memory size of 52428800 bytes exhausted (tried to allocate 23422437 bytes) in Unknown on line 0, referer: http://www.jpvincent.dev/sandbox/upload.php
[Thu Sep 16 16:09:02 2010] [notice] child pid 9807 exit signal Segmentation fault (11)
it says I’m trying to allocate 20Mb (the size of the file) whereas PHP has a memory limit of 50Mb … which is a nonsense !
all the habitual php.ini values are set to values far larger than 20Mb :
upload_max_filesize
max_input_time
memory_limit
max_execution_time
post_max_size
any idea about the solution ?
jpvincent
16 Sep 10 at 15:19
ok, after some testing, I arrived to this conclusion : with this method, the PHP script takes 3 times the uploaded file size in memory, leading to the error I mentioned earlier.
in my case : I had a 22Mo file, PHP required 67Mo in memory
I made tests with 200k, 2M, 22M, and 120M, the 3.1 ratio is constant.
and also on the client side : when the file is big, it takes a lot of CPU, freezing the UI while the getAsBinary() is made
the only viable option is to use DataForm, which works on Chrome5+, Safari5 and Firefox 4 (and some 3.6 nightlies)
jpvincent
17 Sep 10 at 15:23
[...] while back i wrote a post where i explained how to implement the new XMLHttpRequest2 object. The main point of the post was [...]
Drag & drop Uploads with XMLHttpRequest2 and PHP Revised | Web Development and stuff...
22 Sep 10 at 13:26
Many thanks for this post! It saved me much time and allowed me to create a backwards compatible file upload script. The difficulty was that the FormData object isn’t implemented by Firefox 3.6.
Jeremy Caton
1 Jul 11 at 09:29
Good to know it helped somebody. I still need to take a look at the drag & drop issue i was having with the Ubuntu/Firefox combo.
Thijs Lensselink
1 Jul 11 at 09:49