mod_uploadprogress is back

In my ongoing efforts in restructuring the core of lighttpd for the 1.5.0 release I brought mod_uploadprogress back to life.

I talked about mod_uploadprogress a long time ago and since then I got at least one request per month to bring it back again.

It was changed abit since the early days. Instead of XML we send out JSON now. That does the same and easier to parse:

{ 'state' : 'starting' };
...
{ 'state' : 'uploading', 'size' : 87901150, 'received' : 80601372};
{ 'state' : 'done', 'size' : 87901150, 'received' : 87901150};

First you need a file-upload form. It is all standard and only calls our upload-progress updater onSubmit.

<form id="upload" enctype="multipart/form-data"
    action="/upload.php" method="post"
    onsubmit="openProgressBar(); return true;"
>
    <input type="hidden" name="MAX_FILE_SIZE" value="30000000"  />
    <input name="userfile" type="file" label="fileupload" />
    <input type="submit" value="Send File" />
</form>

The progress bar in this case are 2 nested div-tags:

<div>
    <div id="progress" style="width: 400px; border: 1px solid black">
        <div id="progressbar"
            style="width: 1px; background-color: black; border: 1px solid white"
        >
            &nbsp;
        </div>
    </div>
    <div id="tp">(throughput)</div>
</div>

Some Javascript with XMLHTTPRequest can tell us about the state of a upload:

<script type="text/javascript">
interval = null;

function fetch(uuid) {
    req = new XMLHttpRequest();
    req.open("GET", "/progress", 1);
    req.setRequestHeader("X-Progress-Id", uuid);
    req.onreadystatechange = function () {
        if (req.readyState == 4) {
            if (req.status == 200) {
                /* poor-man JSON parser */
                var upload = eval(req.responseText);

                document.getElementById('tp').innerHTML = upload.state;

                /* change the width if the inner progress-bar */
                if (upload.state == 'done' || upload.state == 'uploading') {
                    bar = document.getElementById('progressbar');
                    w = 400 * upload.received / upload.size;
                    bar.style.width = w + 'px';
                }
                /* we are done, stop the interval */
                if (upload.state == 'done') {
                    window.clearTimeout(interval);
                }
            }
        }
    }
    req.send(null);
}

function openProgressBar() {
    /* generate random progress-id */
    uuid = "";
    for (i = 0; i < 32; i++) {
        uuid += Math.floor(Math.random() * 16).toString(16);
    }
    /* patch the form-action tag to include the progress-id */
    document.getElementById("upload").action="/upload.php?" + uuid;

    /* call the progress-updater every 1000ms */
    interval = window.setInterval(
            function () {
                    fetch(uuid);
            },
            1000
    );
}
</script>

Now I only have to commit this code to SVN and make a pre-release :)