Merge pull request #30 from kohana/3.3/bug/travis-composer-no-interaction
[kohana-image.git] / guide / image / examples / dynamic.md
blob2d70260bf0720768d40a42fa91323cef5fe2a000
1 # Dynamic Image Controller
3 In this example, we have images under `/uploads` under the webroot directory. We allow the user to render any image with dynamic dimension and is resized on the fly. It also caches the response for 1 hour to show basic caching mechanism.
5 ## Route
7 First, we need a [Route]. This [Route] is based on this URL pattern:
9 `/imagefly/filename/width/height` - where filename is the name of the image without the extension. This assumes that all images are in `jpg` and all filenames uses numbers, letters, dash and underscores only.
11 This is our [Route] definition:
13 ~~~
14 /**
15  * Set route for image fly
16  */
17 Route::set('imagefly', 'imagefly/<image>/<width>/<height>', array('image' => '[-09a-zA-Z_]+', 'width' => '[0-9]+', 'height' => '[0-9]+'))
18         ->defaults(array(
19                 'controller' => 'imagefly',
20                 'action' => 'index'
21         ));
22 ~~~
24 We ensure that the filename is only composed of letters, numbers and underscores, width and height must be numeric.
26 ## Controller
28 Our controller simply accepts the request and capture the following parameters as defined by the [Route]:
30 * `filename` - without the filename extension (and without dot)
31 * `width`
32 * `height`
34 Then it finds the image file and when found, render it on the browser. Additional features added are browser caching.
36 ~~~
37 <?php defined('SYSPATH') or die('No direct script access.');
39 class Controller_Imagefly extends Controller {
41         public function action_index()
42         {
43                 $file = $this->request->param('image');
44                 $width = (int) $this->request->param('width');
45                 $height = (int) $this->request->param('height');
46                 
47                 $rendered = FALSE;
48                 if ($file AND $width AND $height)
49                 {
50                         $filename = DOCROOT.'uploads/'.$file.'.jpg';
51                         
52                         if (is_file($filename))
53                         {
54                                 $this->_render_image($filename, $width, $height);
55                                 $rendered = TRUE;
56                         }
57                 }
58                 
59                 if ( ! $rendered)
60                 {
61                         $this->response->status(404);
62                 }
63         }
64         
65         protected function _render_image($filename, $width, $height)
66         {
67                 // Calculate ETag from original file padded with the dimension specs
68                 $etag_sum = md5(base64_encode(file_get_contents($filename)).$width.','.$height);
69                 
70                 // Render as image and cache for 1 hour
71                 $this->response->headers('Content-Type', 'image/jpeg')
72                         ->headers('Cache-Control', 'max-age='.Date::HOUR.', public, must-revalidate')
73                         ->headers('Expires', gmdate('D, d M Y H:i:s', time() + Date::HOUR).' GMT')
74                         ->headers('Last-Modified', date('r', filemtime($filename)))
75                         ->headers('ETag', $etag_sum);
76                 
77                 if (
78                         $this->request->headers('if-none-match') AND
79                         (string) $this->request->headers('if-none-match') === $etag_sum)
80                 {
81                         $this->response->status(304)
82                                 ->headers('Content-Length', '0');
83                 }
84                 else
85                 {
86                         $result = Image::factory($filename)
87                                 ->resize($width, $height)
88                                 ->render('jpg');
89                                 
90                         $this->response->body($result);
91                 }
92         }
94 ~~~
96 When the parameters are invalid or the filename does not exists, it simply returns 404 not found error.
98 The rendering of image uses some caching mechanism. One by setting the max age and expire headers and second by using etags.
100 ## Screenshots
102 Visiting [http://localhost/kohana/imagefly/kitteh/400/400](http://localhost/kohana/imagefly/kitteh/400/400) yields:
104 ![Kitten 400x400](dynamic-400.jpg)
106 Visiting [http://localhost/kohana/imagefly/kitteh/600/500](http://localhost/kohana/imagefly/kitteh/600/500) yields:
108 ![Kitten 400x400](dynamic-600.jpg)