Valid images output by PHP always "contain errors", what could be causing this?
A few months ago I wrote a website for a customer using PHP 5.3. It works perfectly on my own LAMP webserver. However, when he tried to install it in his own server (currently an OVH server running DirectAdmin on CentOS 5) he ran into an issue I'm having trouble figuring out.
The website can store images uploaded through forms. Images are watermarked on upload and moved to a directory in the webserver (some metadata is stored in the database, but this isn't related to this issue).
In order to display these images back to the user, a script is used like so:
header("Content-type: image/jpeg");
ob_start();
echo file_get_conte开发者_开发百科nts($path);
$size = ob_get_length();
$img = ob_get_contents();
ob_end_clean();
header ("Content-length: " . $size);
echo $img;
Unfortunately, this always returns a broken image (in Firefox, "The image cannot be displayed because it contains errors"). Now, after careful testing, I know that:
Images are correctly uploaded to the server. The image data stored in the webserver is valid and can be obtained via FTP as the regular image.
If I store $img to a file immediately before the final line of the previous script, like so:
$fh = fopen("test.jpg", "w"); fwrite($fh, $img); fclose($fh);
It will save the correct image data to the file also. So the data is intact immediately before being sent to the user's web browser.
The headers are being sent correctly.
However! If I use a text/plain header rather than image/jpeg, I can see that the gibberish returned is different from the gibberish displayed if I open the file locally with notepad (or send the image directly through apache as a text file). In the original image, I can see some EXIF. In the image generated by PHP and then send to the user's web browser, I still see the JFIF magic code (for JPEG File Image Format) but the rest looks different.
I'm afraid I have a configuration-related issue on either PHP or Apache related to encoding, buffering, content compression or something like that. Does anyone know anything I can try to solve this issue?
EDIT:
Changed the script to use:
$img = file_get_contents($path);
$size = filesize($path);
The issue remains unchanged, but the content now looks exactly the same comparing the real image to the image sent from PHP. According to the headers, the content encoding is gzip. Any ideas?
Well, after some investigation it turned to be infamous Byte Order Mark signature (in conjunction, of course, with output buffering which suppressed an error) in PHP script.
It seems just re-saving the file without BOM will solve the problem
does it work?
header("Content-type: image/jpeg");
echo file_get_contents($path);
or this?
header("Content-type: image/jpeg");
readfile($path);
download this image (using wget or make a link on it and use 'Save as') and see the difference. It may shed the light on the cause
And yeah. ob has absolutely nothing to do here. if you want to get a file size - there is a (surprise!) a function for it
header("Content-type: image/jpeg");
header ("Content-length: " . filesize($path));
readfile($path);
First, if you haven't already I would confirm that the problem image is indeed a jpeg and not some other type of image. Many image viewing programs will display them properly even if given the wrong type or extension but it could still cause issues if you send a jpeg type for a non-jpeg.
Second, I would move the ob_start() to the first line of your file.
Third, while it is best practice to send the Content-length header it doesn't need to be sent so I would remove it just to eliminate one possible source of a problem with invalid data being sent.
Finally, if you have GD installed and it suites your usage requirements this alternate solution may work for you.
ob_start();
$image = imagecreatefromjpeg( $path );
if (!$image ) {
// error trapping / other logic here
}
ob_end_clean();
header( "Content-type: image/jpeg" );
@imagejpeg( $image );
if ( $image ) {
imagedestroy( $image );
}
The correct answer is given by Your Common Sense in a comment, but its a bit unclear so i thought i would post it and elaborate a little:
"ha! that's it! UTF-8 Signature also Byte Order Mask it is! It's in your PHP script. Save it without this signature"
I literally spent hours trying to figure out why the images didn't display in browser - the issue is not only related to the code in the question, but can also appear when using php headers with readfile and file_get_contents.
Thanks to Your Common Sense and Protected for raising the question
I solved it by changing the encoding of the page where the code is placed - UTF-8 seems to be ok, but the BOM (Byte Order Mask) has to go. How you do the change depends on the software you are using for editing - i am using an old version of Dreamweaver in which i can uncheck the use of BOM under "page properties".
精彩评论