CGI's UPLOAD_HOOK
2008/02/02 filed under /perlMany 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? ;-)



Comments
Kim wrote at 2008-02-26 23:01:
B10m wrote at 2008-02-26 23:10:
Yorhel wrote at 2008-04-29 20:20:
B10m wrote at 2008-04-29 20:22: