. . . open( my $fh, '>', 'myprogressdata') or die "bad open: $!"; $fh->autoflush; # no buffering, print writes immediately my $q = CGI->new( \&myhooksub, $fh ); # You'll get control here only after CGI.pm is done # processing form and upload data . . . #### sub myhooksub { my( $filename, $buffer, $bytes_read, $fh ) = @_; seek( $fh, 0, 0 ); # rewind to start of file printf $fh "Uploading '%s', %d out of %d bytes read\n", $filename, $bytes_read, $ENV{CONTENT_LENGTH}; } #### . . . $that_ref_to_his_sub = ... # copied from your $his_other_data_value = ... # call to new() . . . while( $buffer = more_to_read() ) { if( defined $that_ref_to_his_sub ) { $bytes_read += length($buffer); &{$that_ref_to_his_sub}($filename,$buffer,$bytes_read,$his_other_data_value ); } print $fh_tempfile $buffer; } . . . #### #!/usr/bin/perl -w use strict; use warnings; use CGI; my $q = new CGI; print $q->header, $q->start_html( -title => 'Upload Progress', -head => meta({-http_equiv=>'Refresh', -content=>'3'}) ); if( open( my $fh, '<', 'myprogressdata') ) { my $progress = <$fh>; print $q->h2('Upload Progress'), $progress; } else { print $q->h2('No Data Available'), $!; } print $q->end_html;