Many a time, I see people asking and messing with
CGI uploads and
progress bars.
First of all, I believe an upload progress bar is the responsibility of the
browser (client) and not of the server. The client knows the file size
it is uploading and how many bytes it sent over the wire. Regardless,
progress bars are fairly nice, especially with large(r) files. So let's
see how we can implement one.
Perl is very well suited to show you the upload progress (I believe
it's more tricky with PHP), due to the UPLOAD_HOOK facility of
CGI
The documentation isn't too extensive, so let's just look at an example.
First of all, you'd need to understand what needs to be done. After
someone hits the upload button, we need to query the server over and
over, to get the upload status. Javascript kicks in here.
To display the bar, I simply use an existing script, for a) it looks better
than anything I'd ever create and b) it works :)
Bram.us 's
jsProgressBarHandler
is the one I chose for this example.
Ok, so now first take a look at the hook subroutine. First you'd have
to create an instance of the CGI object like this:
my $q = CGI->new(\&hook);
The hook subroutine isn't too fancy either. I use File::Slurp
to write the percentage to a file that we can query later.
sub hook {
my ($filename, $buffer, $bytes_read, $data) = @_;
my $perc = sprint("%i", (($bytes_read / $ENV{CONTENT_LENGTH}) * 100));
write_file("/tmp/$ENV{REMOTE_ADDR}", {overwrite => 1}, "$perc");
}
That's all it takes.
Now, on the frontend, we simple query this file over and over, like this:
function doUpload() {
$('progress').show();
var intervalID = window.setInterval('doProgress()', 1000);
}
function doProgress() {
var d = new Date;
new Ajax.Request('progress.cgi?time='+d.getMinutes()+
'_'+d.getSeconds(), {
method:'get',
onSuccess: function(transport){
myJsProgressBarHandler.setPercentage('progress',
transport.responseText);
}
});
}
The function doUpload shows the progress bar and calls doProgress
every second. Since Internet Explorer seems to think that caching the
Ajax.Request is a smart thing to do, I simply post the minutes and seconds
to the script aswell.
And progress.cgi isn't so fancy either:
#!/usr/bin/perl
use strict;
use File::Slurp;
print "Content-type: text/plain\n\n";
my $perc = read_file("/tmp/$ENV{REMOTE_ADDR}");
print $perc;
This works rather well on my machine(s) and it's really simple, as you
can see. The only downside is that when two people sharing the same
IP address start uploading at the same time, they'll probably get the
wrong information. But hey, who cares? ;-)