Sunday, August 15, 2010: Userscript: Drag and Drop Upload for upic.me, making of « from the old blog archive »

I have just seen upic.me's new style. It looks really nice. At the same moment, I feel that it would be nice to be able to drag and drop pictures from my computer to upload that picture. I knew it was possible because I have seen this. So I started coding and here's the result:

upic.me Drag and Drop Upload

After installing this userscript, try going to http://upic.me/. The box should say "drop here to upload picture" instead of "click there to upload picture." Try draging some pictures from your computer and drop it on the website. The image you dragged should upload right away.

This best thing about this is it handles file uploading by itself. It does not require Flash at all.


Update 2012-06-29: Now we have the FormData API, the following is not needed anymore!

Also, the code here is extremely outdated and uses unsupported nonstandard deprecated APIs such as file.getAsBinary and xhr.sendAsBinary that no longer work in any browser anymore. Just use the FormData API!


If you want to implement this on your own userscript or your website, feel free to copy it from my code. You should also read this to learn how to setup drop boxes: Using files from web applications#Selecting files using drag and drop.

The uploading part is the one of the hardest parts. That is because you need to create the POST request payload by yourself.

To do this, first, you can just set up XMLHttpRequest normally.

    var xhr = new XMLHttpRequest();
    xhr.open ('POST', '/upload.php');
    xhr.onreadystatechange = function() {
        // ...
    };

Then make a progress listener if you want to keep track of the progress.

    xhr.upload.addEventListener ('progress', function(e) {
        if (e.lengthComputable) {
            // e.loaded = amount of data sent
            // e.total = total amount of data
        }
    }, false);

Now we need to prepare the payload the boundaries.

    var payload = '';
    var boundary = '---------------------------53748593789457348248012312';

So I made this 2 utility functions that will help you.

The first function adds a normal field data to the payload. You will use this for normal form input fields, such as hidden or text input.

The second function receives a HTML5 File object and adds it to the payload.

    function addFormField(name, value) {
        payload += '\r\nContent-Disposition: form-data; name="' + name + '"\r\n\r\n' + value + '\r\n--' + boundary;
    }
    function addFile(name, fileName, file) {
        payload += '\r\nContent-Disposition: form-data; name="' + name + '"; filename="' + fileName + '"\r\nContent-Type: ' + file.type + '\r\n\r\n' + file.getAsBinary() + '\r\n--' + boundary;
    }

So after you prepare the payload using these 2 functions, now we have a huge string of data waiting to be sent to the server, so now let's upload it!

    xhr.setRequestHeader("Content-Type", "multipart/form-data, boundary="+boundary);
    xhr.setRequestHeader("Content-Length", payload.length);
    xhr.sendAsBinary (payload);

If you want, you can also see the source code of my userscript. That's it for today's blog post.