tags:

views:

82

answers:

3

When I try to print an image to STDOUT in a Perl CGI script, the image gets clipped when viewed in the browser.

Here is the following code:

if ($path =~ m/\.jpe?g$/i)
{    
  my $length = (stat($path))[7];
  $| = 1;
  print "Content-type: image/jpg\r\n";
  print "Content-length: $length\r\n\r\n";
  open(IMAGE,"<$path");
  binmode(IMAGE);
  binmode(STDOUT);
  my ($image, $buff);
  read IMAGE, $buff, $length;
  syswrite STDOUT, $buff, $length;
  close IMAGE;
}
+2  A: 

EDIT: as the stat doesn't seem to be problem, some more ideas:

try using unbuffered instead of buffered reading, ie. use sysread instead of read. or the other way round: use both buffered read and write. also, try commenting out the $|. see Suffering from Buffering? for details on perl buffered io. see also How can I serve an image with a Perl CGI script? here on SO for an apparently working solution. EDIT END

you are using the wrong stat field. (stat($path))[10] is ctime: inode change time in seconds since the epoch. it should be (stat($path))[7], size: total size of file, in bytes.

ax
It does not matter if I include content-length or not. I changed the stat as you suggested, but still the image gets cut off
JerA
i just tried your (updated) code here: it is working. so i guess it is something with your image. mind to share a link?
ax
could it be a server/installation issue?
JerA
maybe. but i think it's more probable that it has to do with perl's buffered io. see my **EDIT** above.
ax
+4  A: 

If you really want to read the entire file into memory before serving, use File::Slurp:

#!/usr/bin/perl

use strict; use warnings;

use CGI::Simple;
use File::Slurp;
use File::stat;

local $| = 1;

my $cgi = CGI::Simple->new;

my $st = stat($path) or die "Cannot stat '$path'";

print $cgi->header(
    -type => 'image/jpeg',
    -length => $st->size,
);

write_file(\*STDOUT, {binmode => ':raw'}, 
    \ read_file( $path, binmode => ':raw' )
);

However, reading the entire file will consume large amounts of memory for large images. Therefore, see How can I serve an image with a Perl CGI script?.

Sinan Ünür
if he calls `read` with the `$length` of the image, isn't it read in just one call?
ax
i read the doc. and i do see a need for returning the number of characters actually read: when reading at the end of the file. example: a file of 240 byte, with `$length` 100: first read returns 100, second read 100, third/last read 40. however, when `$length` is the size of the file, `read` should read the whole file or return an error, but not something in between. shouldn't it?
ax
@ax You are correct. Thank you. Cleaning up my post and comments. The OP still needs to check the return value of `read` and `syswrite`, though.
Sinan Ünür
A: 

FYI: I have come to the conclusion that the images are in fact corrupt, though they are fully viewable in Windows File Explorer.

The FireFox browser shows the Images clipped(no matter how they are accessed, so I guess this is no longer a Perl problem), but the Safari Browser displays them completely.

The images were re sampled from using Java's imageIO in "jpg" mode. I just changed the mode to "png", and now the newly generated images are showing perfectly in all browsers. So this was actually a Java imageIO issue.

It is solved.

Thank you everyone for your responses.

JerA