tags:

views:

416

answers:

4

can anyone advise on what the best PHP function is for displaying an image stored on the filesystem - file_get_contents or readfile. We are switching from displaying images stored in the database so we still need to call the images through a PHP file and cannot link direct to the filesystem. I've seen people recommending both functions but i'm inclined towards using readfile. would appreciate any input on this.

+3  A: 

I would do something like:

header('Content-type: image/jpeg');
readfile('something.jpg');

readfile() seems more useful for this purpose as it reads a file and writes it to the output buffer returning the number of bytes read from the file, or false on an error.

karim79
cool thanks, thats exactly what we're doing
seengee
fpassthru does the same with fopen's file handler.
Jet
+5  A: 

If you don't need to manipulate the images (resizing, adding watermarks,...) readfile() will be the best choice as it writes the file directly to the output buffer. file_get_contents() will read the file into memory instead - requiring a lot of memory with large files.

Stefan Gehrig
my sentiments exactly. +1
Here Be Wolves
thanks for the explanation guys
seengee
A: 

I should use fpassthru Using fopen you can handle errors more precisely.

PS: be sure, that your direct output from filesystem is not vulnerable!

Jet
good point, have made sure its very secure though
seengee
+4  A: 

Be sure to take caching into account when writing scripts like this!

A webserver serving a static image will do negotiation with the client when the image is re-requested on the next page visit, and if the server determines that the cached copy of the image at the client side is still valid, the image will not be retransmitted.

Since the naive script does not do this negotiation, the image will be retransmitted to the client on every page request, costing you a lot more bandwidth than necessary.

There are three mechanisms for this. I can't tell you exactly how to write the optimal script, as I've never had to do this before and I'm not sure how the different caching headers interoperate and on what HTTP version, but I encourage you to research into this further.

The three mechanisms that I know of:

Expires (HTTP/1.0)

The simplest one. This header tells the client that the image will definitely be valid until the given moment in time. The client will not even do a request to the script until this time has passed, so setting this appropriately can save you (some) CPU cycles on the server and image loading latency in your web app.

How you should set this depends entirely on your application; are your images changing rapidly or rarely? If the image changes before the Expires time you've sent to the client, the client will not see the new image.

Example:

header("Expires: " . gmdate('D, d-M-Y H:i:s \G\M\T', time() + 60)); // Valid for a minute

(Note: Expires seems to have been superseded by Cache-Control in HTTP/1.1)

If-Modified-Since (HTTP/1.1)

An HTTP/1.1 client can send this header if it already has a copy of the image, and notes what time the copy dates from. You can then determine in your database if the current version of the image was changed at an earlier or later time. If the version of the client is still the right one, simply send a "304 Not Modified" response and quit (thereby preventing having to transfer the image).

Example:

$cache_time   = parse_browsers_date_time_format($_SERVER["IF-MODIFIED-SINCE"]);
$actual_time  = get_current_resource_time_from_db();

if ($actual_time <= $cache_time) {
    header("HTTP/1.1 304 Not Modified");
    die;
}

// ... Produce and output resource here

(Note: clients may only actually send the If-Modified-Since if you also send Last-Modified in the original response. I'm not sure about that, research for yourself.)

ETag/If-None-Match (HTTP/1.1)

This method is similar to the If-Modified-Since negotiation, but instead of times it uses a hash of the image as to see if the content has changed. It works as follows: the server calculates some hash for the image, and sends this hash the first time the image is requested in the ETag header.

On subsequent requests, the server will send the hash back in the request field If-None-Match. If the hash of the client is the same as the current hash of the image, the image was not changed in between and the script can suffice by simply sending "304 Not Modified".

Since ETags seem to be actually intended to be used to prevent concurrency issues in client requests with side effects (i.e., POST and PUT), and because calculating a hash is a costly operation, I think the If-Modified-Since approach will be a better fit for most file-serving applications.

rix0rrr
we've been looking into this today, thanks for all the ideas.
seengee