Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Re: Upload hook syntax and location

by shenme (Priest)
on Dec 28, 2004 at 20:48 UTC ( [id://417841]=note: print w/replies, xml ) Need Help??


in reply to Upload hook syntax and location

Hello, working with this right now myself.

The first three parameters to the upload_hook() routine are supplied by CGI.pm. The last fourth parameter is optionally provided by you in the new() call, and is passed to the hook routine as a convenience for you.

  • $filename - is the value from the file input form field, supplied by the browser, and thus might be just the remote filename or might be the whole remote path to the file
  • $buffer - really is the current buffer contents; I thought it'd be at least a ref to the buffer, but it really is the whole 4KB thing (ugh); the data here will be written to CGI.pm's temp file; I'm not sure what use it is to look at it
  • $bytes_read - this is the cumulative total number of bytes read so far; not just the number of bytes read into this buffer
  • $data - this can be any value supplied by you in the call to new(), so you could use it anyway you wish; I use it to pass an object reference so that my upload_hook() routine can reference the master data structures
In my recent code I am doing
my $q = CGI->new( \ &WhizBang::upload_hook, $self ); # where $self is a ref to the current WhizBang object # then later sub upload_hook { my $remote_filename = shift; my $buffer = shift; my $bytes_read = shift; my $self = shift; my $progress = $self->param('progress'); my( $filename ) = $remote_filename =~ m/^ (?: .* [:>\]\\\/] )? ( .* +) $ /xs; $progress->{file_name} = $filename; $progress->{bytes_xfrd} = $bytes_read; $progress->{time_now} = time(); # other processing to output data to external storage # for display from another script }
Elsewhere I grab the value in $ENV{CONTENT_LENGTH} to compare against the $bytes_read value.

I have a couple of quibbles about this recent addition to CGI.pm, but at least the optional last $data parameter makes finding and updating your 'home' data structure possible.

As far as what you can do with this, yes, you pretty much must capture the data and write it to some place that _another_ script can find it for display. Once CGI.pm starts processing the POST'ed data (when you call new()) it will not return until all nnnMB and nn files are done being received. I'm writing the progress information to a database for a Javascript spawned window to find.

Some of these recent posts have references to examples:
Upload Progress Bar
Deferred/selective processing in CGI.pm
Upload Progress
Re: Re: CGI.pm and Large File Transfers

Replies are listed 'Best First'.
Re^2: Upload hook syntax and location
by Anonymous Monk on Dec 29, 2004 at 03:30 UTC
    Thanks a lot, Shenme.

    Now I understand the parameters. But, and forgive my difficulty in this matter, I still canīt understand how to make the upload.cgi script give-me partial info (for me to save it to a file and get another script to read, ok) about the download while he is uploading it. Isnīt it stuck in the line below untill it finishes?
    my $fh = $q->upload( "file" ); # 'file' is the name of the field of the form where the user has selected the file.
    And, I also donīt know if I shall call the upload_hook before, during or after this line above. I am not familiar with objects - just enough to deal with the modules -; can you, or any of the monks, give me a example of a non-object code including the upload() call above and the hook info-extraction?

    Thanks a lot, you all!

    André

      Some of the other nodes I referenced above give more background information.

      The problem is indeed that once you call CGI->new() you are 'trapped' until CGI.pm has processed _all_ the form data including uploaded files. But CGI.pm has added this optional 'hook' feature that allows you to specify one of your routines for CGI.pm to call _during_ the processing of the form data.

      You call CGI.pm and it runs until done, but each time it processes some more upload data it calls your routine. In your code you could do something like this: (untested)

      . . . 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 . . .
      Somewhere else in your same code you define your routine that CGI.pm will call for each chunk of data. Here we rely on that extra parameter value that CGI.pm copies from the new() call to pass into your routine. We open the file, give the filehandle to CGI.pm, and CGI.pm adds that to every call to your routine:
      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}; }
      CGI.pm will call this routine many times, once for each chunk of the upload data it processes, something like: (in CGI.pm)
      . . . $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; } . . .
      Note that value $ENV{CONTENT_LENGTH} will really be larger than the uploaded file sizes, as it includes the rest of the form data also, but it's close and the only 'total' number we have (sigh).

      Your display CGI could be as simple as:

      #!/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;

      Now this example used files (I'm currently using a database table). You might want to use file locking (see flock in perlfunc and do SuperSearch's) to make sure the display CGI doesn't read a partially written record, though with one line it seems unlikely.

      Anyway, I hope this helps, and that I haven't confused you more than before.

      Sorry, I was not logged in when I posted the reply. Sending this one just to say everybody I answered with other questions.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://417841]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others goofing around in the Monastery: (3)
As of 2024-04-20 14:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found