Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Headers and Caching in php

I have a small image generator as part of my laravel4 application. It takes about 700ms to generate the image and so I have started caching the generated result on my server and returning that to the browser instead which saves some time.

As the image will never change once generated I wanted to tell the browser to cache the image locally and I have done this with the following code:

$path = $cacheFolderPath . $cacheFileName;

if (File::exists( $path )){
    $response = Response::make(File::get($path));
    $response->header('Content-Type', 'image/png');
    $response->header('Content-Disposition', 'inline; filename="'.$cacheFileName.'"');
    $response->header('Content-Transfer-Encoding', 'binary');
    $response->header('Cache-Control', 'public, max-age=10800, pre-check=10800');
    $response->header('Pragma', 'public');
    $response->header('Expires', date(DATE_RFC822,strtotime(" 2 day")) );
    $response->header('Last-Modified', date(DATE_RFC822, File::lastModified($path)) );
    $response->header('Content-Length', filesize($path));
    return $response;
}

This sends an image with status code 200 OK to the browser with the following headers:

Cache-Control:max-age=10800, pre-check=10800, public
Connection:Keep-Alive
Content-Disposition:inline; filename="pie_0_normal.png"
Content-Length:2129
Content-Transfer-Encoding:binary
Content-Type:image/png
Date:Wed, 07 Aug 2013 10:29:20 GMT
Expires:Fri, 09 Aug 13 10:29:20 +0000
Keep-Alive:timeout=5, max=93
Last-Modified:Wed, 07 Aug 13 10:14:42 +0000
Pragma:public
Server:Apache/2.4.3 (Win32) OpenSSL/1.0.1c PHP/5.4.7
Set-Cookie:laravel_session=767487mhf6j2btv3k01vu56174; expires=Wed, 07-Aug-2013 12:29:20 GMT; path=/; httponly
X-Powered-By:PHP/5.4.7

My issue is that my browser (chrome, not tested in others) still refuses to simply grab the local cached version and instead hits the server again.

I have spent about half an hour searching for other questions on this subject and all of them have given me answers which I have incorporated into the above code. So while I know that there are similar questions, this one is unique to the above source code.

My question is, what am I doing wrong that would result in the file not being cached by the browser?

like image 344
carbontwelve Avatar asked Aug 07 '13 10:08

carbontwelve


People also ask

Does Laravel have caching?

Laravel supports popular caching backends like Memcached, Redis, DynamoDB, and relational databases out of the box. In addition, a file based cache driver is available, while array and "null" cache drivers provide convenient cache backends for your automated tests.

What is caching in Laravel?

Laravel provides a unified API for various caching systems. The cache configuration is located at app/config/cache. php . In this file you may specify which cache driver you would like used by default throughout your application. Laravel supports popular caching backends like Memcached and Redis out of the box.

What is php caching?

A cache is a collection of duplicate data, where the original data is expensive to fetch or compute (usually in terms of access time) relative to the cache. In PHP, caching is used to minimize page generation time.

How cache is implemented in Laravel?

To enable Laravel caching services, first use the Illuminate\Contracts\Cache\Factory and Illuminate\Contracts\Cache\Repository, as they provide access to the Laravel caching services. The Factory contract gives access to all the cache drivers of your application.


2 Answers

An alternative method to this would be to check for the 'If-Modified-Since' request header as it will only be present if the browser already has the file.

If it is present, then you know the file is already created and can respond with a link to it, otherwise run your code above. Something like this...

// check if the client validating cache and if it is current
if ( isset( $headers['If-Modified-Since'] ) && ( strtotime( $headers['If-Modified-Since'] ) == filemtime( $image->get_full_path() ) ) ) {

    // cache IS current, respond 304
    header( 'Last-Modified: ' . $image->get_last_modified(), true, 304 );

} else {

    // not cached or client cache is older than server, respond 200 and output

    header( 'Last-Modified: ' . $image->get_last_modified(), true, 200 );
    header( 'Content-Length: ' . $image->get_filesize() );
    header( 'Cache-Control: max-age=' . $image->get_expires() );
    header( 'Expires: '. gmdate('D, d M Y H:i:s \G\M\T', time() + $image->get_expires() ) );
    header( 'Content-Type: image/jpeg');

    print file_get_contents( $image->get_full_path() ); 
}
like image 163
darronz Avatar answered Sep 21 '22 12:09

darronz


It appears that you are still telling Laravel to download the asset and serve the response as a download, you can provide a Cache-Control header, but it does not matter because you are re-generating this asset each time the image is loaded.

I ran into a similar problem myself and found the best way to serve the images was to inject a static function into my view something like ImageController:fetchImage($image).

The method then will then check a local folder to see if the file exists and then it will just return the location of the image, something like: /img/1332.jpg... If the file does not exist it will create the image, save it to the img folder and then return the location.

This avoids laravel from ever having to serve up a brand new (uncached) response as a download EACH time the image is requested and instead directs the browser to load resources that are already cached.

This worked in my case that is...

    if (App::environment('local'))
    {
       return 'http://placehold.it/150x150';
    }


    $target_file = 'img_cache/'. $release_id .'.jpg';

    if (file_exists($target_file)) return '/'.$target_file;
    else
    {
        $release = DB::(get release location on another server from db);
        if(!$release)
            return 'http://placehold.it/150x150';

        $imageString = file_get_contents("http://pomed.promoonly.com/".$release[0]->image_src);

        file_put_contents($target_file, $imageString);

        return '/'.$target_file;
    }
like image 23
Joey Avatar answered Sep 21 '22 12:09

Joey