saberworks has asked for the wisdom of the Perl Monks concerning the following question:

I would like to create a new GD::Image with a file uploaded through a web page. The CGI.pm documentation says that files are put in a tmp directory with a filename like CGIXXXXX. I was wondering how I could get direct access to that file without first copying it to another directory. The GD::image constructor says it can take the following types of files:
1) a simple filehandle, such as STDIN 2) a filehandle glob, such as *PNG 3) a reference to a glob, such as \*PNG 4) an IO::Handle object 5) the pathname of a file
I have tried passing the $fh directly from $cgi->upload('my_file_field'), but I get errors like: Bad file descriptor. I would really like to load the image directly instead of having to copy it to a different directory first. Is this possible?

Replies are listed 'Best First'.
Re: Creating GD::Image directly from CGI->upload()
by tachyon (Chancellor) on Oct 21, 2004 at 02:46 UTC

    CGI.pm uses its own hand rolled tempfile mechanism which is almost certainly the issue. See the package Fh around line 3000 of CGI.pm to see how this is hand rolled. If you are not using the HTML features then CGI::Simple gives you the same API but returns real IO::File objects which will work.

    You can get the actual filename but you will have to hack into the CGI.pm object to do that. I suggest you don't. But if you want to Data::Dumper will let you see what you need to do to access the actual filename (it is under the .tmpfiles hash key in the object root). NB If $PRIVATE_TEMPFILES is set true then CGI.pm will unlink the underlying file, so you can only access it via the open handle that CGI.pm maintains.

    If you want my 2c the simplest change is just to copy the file to a tmp file, make your GD:Image and unlink it. In relative terms the wait for the upload is the vast majority of the execution time, generating a tmp file is a trivial cost.

    cheers

    tachyon

      Thanks tachyon. Yeah I guess I'll just do that - I would use CGI::Simple (I don't like CGI.pm much at all), but the code I'm working on was written by someone else and while they don't usually use the html generation parts of CGI.pm, I've seen a smattering of it here and there, so it's not really safe for me to do a drop-in replacement. I don't want to mess around with the internals of CGI.pm - if I do that I'll be even further away from my goal of eventually dropping it altogether.
Re: Creating GD::Image directly from CGI->upload()
by hostyle (Scribe) on Oct 21, 2004 at 09:26 UTC

    Update: Sorry - I was half asleep and did not read the question properly. I'll leave the code all the same

    Old and untidy code :( I've trimmed out some parts (checking if file of same name already exists, for example)

    sub upload_file { my $upfilename = $q->param('filebrowser'); $upfilename = (split(/\\/, $upfilename))[-1]; my $upfile = $q->upload('filebrowser'); if (!$upfile && $q->cgi_error) { print $q->header(-status=>$q->cgi_error); } else { $filepath = "$images_directory/$upfilename"; open (NEWFILE, ">$filepath") or &error_code("Could not save to + $filepath"); binmode NEWFILE; my ($mbytesread, $mbuffer, $mBytes); while ($mbytesread = read($upfile, $mbuffer, 1024)) { $mBytes += $mbytesread; print NEWFILE $mbuffer; } close(NEWFILE); if ($mBytes <= 0) { &error_code("Uploaded file is 0 bytes in size and will be +discarded."); unlink $filepath; } # create thumbnail use GD; use Image::GD::Thumbnail; # Load your source image open (IN, "<$filepath") or &error_code("Could not open $filepa +th to create thumbnail"); my $srcImage = GD::Image->newFromJpeg(*IN); close(IN); # Create the thumbnail from it, where the biggest side is 200 +px my ($thumb,$x,$y) = Image::GD::Thumbnail::create($srcImage,200 +); # Save your thumbnail open (OUT, ">$thumbs_directory/$upfilename") or &error_code("C +ould not save to $thumbs_directory/$upfilename"); binmode OUT; print OUT $thumb->jpeg; close(OUT); }
      Thanks for the tip on Image::GD::Thumbnail. I actually used it in this project, but I think I'm going to modify it to allow me to constrain width or height independently and calculate the other dimension automatically. It would definitely be more useful that way.