http://qs321.pair.com?node_id=234277

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

I have a script which contains several links like so:
<img src="cgi-bin/image_serve.cgi?full+124236+pc_img1">
where the arguments change for every link.
'full' is the size of the view, '124236' is the ID (and the primary key in the database) and 'pc_img1' is the location.

The script returns an image, which is then put into the page on the fly. There are multiple different images on the page, all presented by the same program.

My concerns is that calling the 'image_serve.cgi' multiple times from the same page with different arguments is causing some kind of problem. Perhaps it simply takes too long for some of the images and the server times out?

When viewing the page with the multiple calls, some images show up, some do not and show broken link placeholders. This changes at random and will differ each time the script is run. Also, occasionally, all images show up. After the page is done loading, one can right-click and "Show Picture" on the images and they will display, so the data does exist.
The code for my 'image_serve.cgi' is below:

#!/usr/bin/perl BEGIN { $|=1; use CGI::Carp('fatalsToBrowser'); } use CGI qw(:all); use DBI; use DBD::mysql; use Image::Magick; my $database = "###"; my $username = "###"; my $password = "###"; my $hostname = "###"; my $tbl = "listings"; my $query = new CGI; my $location = $ARGV[0]; my $mls_id = $ARGV[1]; my $img = $ARGV[2]; my $dbh = DBI->connect("DBI:mysql:$database:$hostname",$username,$pass +word) or die "Unable to connect to listings Database: $DBI::errstr\n" +; my $sql = "SELECT $img FROM listings WHERE pc_mls_id = $mls_id"; my $sth = $dbh->prepare($sql) or die "Couldn't prepare: $DBI::errstr\n +"; $sth->execute; my $ref = $sth->fetchrow_hashref; my $raw_img_data = $$ref{$img}; $sth->finish; $dbh->disconnect; my $image = Image::Magick->new; open(IMAGE, $raw_img_data); $image->Read(file=>\*IMAGE); close(IMAGE); my ($x,$y); if ($location eq "detailed"){ $x = 250; $y = 226; } if ($location eq "med"){ $x = 150; $y = 113; } if ($location eq "small"){ $x = 75; $y = 57; } $image->Resize(width=>$x,height=>$y); print "Content-type: image/jpeg\n\n"; $image->Write('jpg:-');
Anything glaring I'm missing, or is the problem on the server or client end, and not Perl's problem?
Thanks,
cidaris

Replies are listed 'Best First'.
Re: Multiple Script calls
by Zaxo (Archbishop) on Feb 11, 2003 at 00:02 UTC

    Do the error logs show anything?

    One possibility is that you are running out of database connections, or else saturating the mysql server to the point of timing out. Add error checking to your DBI calls to see if that is the case. You probably would prefer missing images to page death, so the RaiseError attribute is contraindicated. Just add the same sort of error checking you use for the connection to the statement handle's execute method.

    Update: "Premature end..." means the script exited before printing the headers. That means it hit die somewhere, a fatal runtime error, or else didn't compile correctly. Have you tried running from the command line? If it compiles correctly, the problem is likely to be dbi related. Try to get some useful messages into the log with diagnostics, Carp, etc.

    After Compline,
    Zaxo

      Multiple entries for "premature end of script headers" that I assume comes from the fact that CGI itself isn't printing a header? I would think that having the line:
      print "Content-type: image/jpeg\n\n"
      would prevent this problem?
      cidaris
Re: Multiple Script calls
by cees (Curate) on Feb 11, 2003 at 02:35 UTC

    Can't really see anything that would cause problems... I would pepper the code with debug statements and look at the log files to see where things are dying.

    I have couple of comments on the code itself though. You really should remove the use CGI::Carp('fatalsToBrowser'); since this script returns an image and fatalsToBrowser generates HTML. And more importantly, you should be using placeholders in your SQL statement. You take a value provided by the browser and plug it straight into your SQL statement without checking anything. Changing it to something like the following will make things much safer:

    my %IMG_FIELDS = { field1 => 'Field1', field2 => 'Field2', }; die "Invalid img parameter $img" unless $IMG_FIELDS{$img}; my $sql = "SELECT $IMG_FIELDS{$img} FROM listings WHERE pc_mls_id = ?" +; my $sth = $dbh->prepare($sql) or die "Couldn't prepare: $DBI::errstr\n"; $sth->execute($mls_id);

    You can't use a placeholder to select a column dynamically (at least my quick test in PostgreSQL didn't allow it), so we use a lookup table for that one. For the pc_mls_id we use a placeholder. The value automatically gets quoted by DBI to protect it from any nasties that your users may put in there.

      Using placeholders in the scripts that call this 'image_serve' and in the script that actually allows the database to be modified... Skipped placeholders in this one to keep it slightly simpler. Will replace them later.

      Thanks for the tip, though.
      cidaris