WWW FAQs: How do I output images from a Perl/CGI or PHP script?

2007-06-14: There are two perfectly acceptable solutions. The best choice depends on what you're trying to accomplish.

Solution One: write the image to a temporary file in a web-accessible directory, and generate an <img> element with a src URL that points to that file. Periodically clean up the oldest temporary files. This is the simplest method and works well for many purposes. Temporary file cleanup can be automated, with a Unix cron job, for example.

Solution Two: generate an <img> element with a src attribute that points directly to your Perl/CGI or PHP script, with GET-method form parameters (name=value pairs, separated by &, after the ? in the URL) which tell your script what to do (what image to deliver, based on what criteria). For example:

<img src="myscript.pl?imageid=100&generateimage=1">

The myscript.pl script can then look at the generateimage and imageid parameters exactly as it would if they had been submitted via a form. This provides the script with enough information to understand what it should do when the browser, having received the HTML page, reinvokes the script to get the image. Your script must then output the following, assuming that you want to emit an image in the JPEG format:

Content-type: image/jpeg

[JPEG IMAGE FILE DATA GOES HERE]

In CGI.pm-based Perl CGI scripts that use the GD library to create images, the code might look like this:

use strict;
use CGI;
use GD;

my $query = new CGI;

if ($query->param('generateimage')) {
 &generateImage;
} else {
 # We weren't asked for the image,
 # so let's return a simple web page
 # that contains the image
 print $query->header;
 print <<EOM
<img src="myscript.pl?generateimage=1">
EOM
;
}

sub generateImage
{
 # We MUST do this on Windows or the
 # image will be garbled, and it
 # doesn't hurt on Unix/Linux/etc  
 binmode STDOUT;
 # Output the right content type
 # for a PNG-format image
 print $query->header("image/png");
 # Draw an image with a red rectangle
 # in the middle
 my $image = new GD::Image(100, 100);
 my $black = $image->colorAllocate(
  0, 0, 0); 
 my $red = $image->colorAllocate(
  255, 0, 0); 
 $image->filledRectangle(
  25, 25, 75, 75, $red);
 # Output the image to the browser
 print $image->png;
}

A PHP version of the above script looks like this. PHP is not always compiled with GD support. Please note that you ABSOLUTELY MUST NOT place HTML in the file that contains this script, prior to the PHP code! Otherwise, PHP will assume you wish to output a page, NOT an image. The trick is to conditionally output HTML only when an image has not been asked for.

<?php
if ($_GET["generateimage"]) {
 header("Content-type: image/png");
 $im = ImageCreate(100, 100);
 $black = ImageColorAllocate($im, 0, 0, 0);
 $red = ImageColorAllocate($im, 255, 0, 0);
 ImageFilledRectangle($im,
  25, 25, 75, 75, $red);
 # Output the image to the browser
 ImagePng($im);
} else {
?> 
<!-- We were not asked for an image, so generate a
 trivial HTML page that contains the image -->
<img src="myscript.php?generateimage=1">
<?php
}
?> 

Of course, you don't have to generate images on the fly with the gd library. You might also wish to generate your images with other tools, or retrieve them from a database. The crucial things are to:

1. Output the correct Content-type: header, and

2. Set standard output into binary mode with binmode STDOUT; if using Perl, before

3. Outputting the image to standard output.

"But I just want to output an existing image from a file..."

But you don't want to link to it directly, right? This situation usually comes up when you only want the image to be accessible when your code decides that's appropriate.

In this case, all you have to do is access the file using the file-access functions of PHP (or your language of choice). Here's a simple example:


<?php
// The above must be the VERY FIRST LINE OF THE FILE!

// Check whether the user is correctly logged in, etc.
// at this point - that depends on YOUR reasons for
// doing this, of course

// Figure out which image to send them
$image = $_GET['image'];
// MAKE SURE IT IS SAFE! Allow only letters,
// numbers, and underscores in the filename,
// don't let them sneak around with ../ and
// download other files on the system!
if (!preg_match("\^\w+$\", $image)) {
  header("Content-type: image/gif");
  readfile("/path/to/access_denied.gif");
  exit(0);
}
  
// OK, they're cool, send them the image.
// /path/to/my/images should be OUTSIDE
// your web space, somewhere else in the
// file system, because the whole point is to
// keep people from downloading what they
// don't have permission to download!
header("Content-type: image/jpeg");
readfile("/path/to/my/images/$image");
exit(0);
?>

Place the above code in image.php. Change /path/to/my/images to an appropriate folder where you have stored your images. Then, in a separate page called test.php in the same folder with image>php, just use this code:


<img src="image.php?image=sample.jpg">

If you have an image called sample.jpg in your images folder, it will appear in the appropriate place when you access test.php with your browser. Then just add appropriate code to image.jpg to make sure the user is authorized to see the image. For instance, you could use my Accountify system to add accounts to your website, as described in my article how do I add accounts to my website. Upon payment for services, you might set the paid attribute in $_SESSION:


$_SESSION['paid'] = true;

Then, at the top of image.php, just make sure the user has paid before continuing:


if (!$_SESSION['paid']) {
  header("Content-type: image/gif");
  readfile("/path/to/unpaid.gif");
  exit(0);
}

Conclusion

Now you have the skills to generate images on the fly, and to deliver existing images dynamically from your own code. You can also leap over tall buildings with a single PHP function call. Enjoy your X-ray vision!

Share |

Legal Note: yes, you may use sample HTML, Javascript, PHP and other code presented above in your own projects. You may not reproduce large portions of the text of the article without our express permission.

Got a LiveJournal account? Keep up with the latest articles in this FAQ by adding our syndicated feed to your friends list!


Follow us on Twitter | Contact Us

Copyright 1994-2014 Boutell.Com, Inc. All Rights Reserved.