I don't think it's possible to do a "meter" (ie, a graphical slider) because
CGI.pm does not know the expected size of the file in advance. It is
possible, however, to do a progress indicator where the client sees the
total number of bytes (or KB or MB or whatever you like) received so far
(and, at least this gives some sense of progress during a very large upload,
and this seems more specifically what the OP was looking for). It requires a
little bit of JavaScript as well, though. You could do this in the main
browser window or a pop-up window (but pop-ups can be problematic with
modern browsers that might be set to disallow them).
Here are some code fragments from an ancient CGI that I wrote years ago. The
use of subroutine prototypes isn't really correct, and it uses
"intermediate" variables that I've since come to avoid, and there are some
other questionable programming practices, but hey, it works....
This is assuming the CGI inherits a multipart form that includes a parameter
such as this (for uploading up to eight files at once; this occurs within a
table (gads, these days I would use CSS)):
for (1..8) {
print Tr({-align => 'center'}, td({-colspan => '3'}, filefield(-name =>
'file', -size => '60')))
}
Now you can handle that upload such as this:
use Number::Format qw/number_format/; #or use sprintf, but I'm too lazy to
remember the syntax
#first, we need a named textfield that JavaScript will use to provide
message updates...
#... this is a form, BUT one that will be "filled out" by the program, not
by the client...
print
start_form ({name=>'status'}),
center(textfield ({name=>'info', -size => 80})), br(),
end_form();
#now upload the file - standard CGI.pm stuff, except we add a call to a
subroutine to update the message field...
foreach $file(param('file')) { #allows for more than one file uploaded at
a time
$file =~ s/^\s+//; $file =~ s/\s+$//;
next unless $file;
$_ = $file;
s!\\!/!g; #Convert DOS \ to UNIX /
s!/+$!!; #Strip off trailing / if any
s/\.zip$/.zip/i; #make sure it's lowercase .zip (if applicable), for
various reasons
s!^.*/!!; #Strip off path, if any
s/\.z$/\.Z/; #change .z extension (if any) to uppercase, for various
reasons
$localfile = &UniqID.".$_"; #supply your own method for uniqueness -
maybe an IO::All call?
$localfile =~ s/[^\w\.\-]/_/g; #Change funny characters to underscore
open (OUTFILE, ">$localfile") || die "Unable to open for write:
$localfile\n";
$count = 0;
while ($bytesread=read($file, $buffer, 1024)) { #1K increments
$count += $bytesread; $count2 = format_number (($count / 2**20), 2,
2); #megabytes
print OUTFILE $buffer;
&Progress("Receiving $file ($count2 MegaBytes received so far)");
#this is where the magic happens!
}
$size = (stat($localfile))[7];
&Progress("File $file Received - $size bytes");
}
sub Progress() {
#This simply updates the named form-field (using JavaScript) with
whatever message we wish...
my $message = shift;
print "<script language='JavaScript'>",
"document.status.info.value=' $message';",
"</script>";
}
Cheers!