
Today, in almost any web application that uses images, there is a need to form reduced copies of these images with some possible additional modification, for example: watermark, shades of gray, sepia, etc.
For details, let's designate such a list of requirements:
- resize images for any size (adding new sizes should not cause a headache)
- image modification: adding a watermark, applying effects to grayscale, sepia and generally adding new effects should not be a difficult task
- image processing should not affect the main thread (page loading speed)
- To speed up the loading of images on the page, the solution should allow to bypass the limit of simultaneous connections in browsers, more about the limit ( rus )
- to avoid the possibility of clogging the server by explicitly passing the resize parameters to the url
- cache the results of work
Decision
To begin with, I argue the choice of means for solving this problem. Among the considered libraries for working with images in PHP, I identified 3 main ones:
Wideimage ,
PHPThumb ,
Imagine . The last one was my choice, since the first two have not been updated for a very long time and it is not clear whether. I chose
Twig as a template engine for which I adapted this solution. Well, I chose Nginx and Apache as web servers. Nginx - for dedicated servers, and Apache for performance on the ball hosting.
GitHub Repository')
Ready solution looks like this:
in PHP
echo thumb(__DIR__ . '/images/Chrysanthemum.jpg', '200x100', [ "watermark" => "right top" ]);
on Twig
<img src="{{ image|thumb("200x200", { "watermark": "right bottom", "grayscale": true }) }}" />
For this filter to be available, you need to connect an extension for Twig.
$twig->addExtension(new \Bazalt\Thumbs\Extension());
1. Resize images for any size
As you can see, the second parameter of the
thumb
function takes the size of the image, the default algorithm is to cut the edges to the specified size, if after a proportional reduction in size, one of the sides climbs. If one of the parameters is set to 0, the image will be proportionally reduced, and the size of the second side will be proportionally calculated, for example, an image with a size of 400x300 with a size parameter of '200x0' will have a size of 200x150 and with a size of '0x200' - 266x200.
2. Image modification
The extension of the image modification functionality is made very simple. There is an
Operations class, which in the basic version has only the
size
function, in order to add its functionality it just needs to inherit and describe the necessary functions in it. The third parameter of the function
thumb
is responsible just for calling these additional modifiers, this is an array of the key which is the name of the function, and the value of the option that will be passed to this function.
An example of several modifiers class Operations extends \Bazalt\Thumbs\Operations { public function watermark(\Imagine\Image\ImageInterface $image, $options, $allOptions) { $imagine = new \Imagine\Gd\Imagine(); $wm = $imagine->open(__DIR__ . '/images/watermark.png'); $size = $image->getSize(); $wmSize = $wm->getSize(); list($x, $y) = explode(' ', $options); if (!is_numeric($x)) { $x = ($x == 'right') ? ($size->getWidth() - $wmSize->getWidth()) : 0; if ($x < 0) $x = 0; } if (!is_numeric($y)) { $y = ($y == 'bottom') ? ($size->getHeight() - $wmSize->getHeight()) : 0; if ($y < 0) $y = 0; } $point = new \Imagine\Image\Point($x, $y); return $image->paste($wm, $point); } public function grayscale(\Imagine\Image\ImageInterface $image, $options, $allOptions) { $image->effects()->grayscale(); return $image; } public function sepia(\Imagine\Image\ImageInterface $image, $options, $allOptions) { $image->effects() ->grayscale() ->colorize(new \Imagine\Image\Color('#643200')); return $image; } }
3. Removal of image processing from the main stream
It is bad to process the image immediately in the place where it is found in the code, because as the number of images grows, the page loading speed grows. The solution is simple - the generation of the processed image only upon request to it. There are a lot of ready-made solutions on the Internet, which differs from mine already created by the fact that I propose to save the image processing parameters not in the url as GET request parameters or parts of the url itself, but to create some configuration file and write the path to the image, dimensions thumbnails and all other options. Looking ahead, I immediately want to note that this also solves point 5 of the requirements.
4. Bypassing the limit of simultaneous connections in browsers
According to the HTTP 1.1 standard, the browser cannot load at the same time more than 2 requests from the same domain. Decision? Make several subdomains for static (
cookieless domain ) at the same time and traffic will be saved due to the fact that extra cookies will not be transmitted.
You can use one of 3 options in the code:
On a large project, a CDN option is being considered, but this is beyond the scope of this article.5. Avoiding the possibility of server clogging by explicitly passing the resize parameters to the url
As partially described in paragraph 3 in the url for which the thumbnail is generated, there are no explicit indications of the parameters, which protects against busting the parameters in the url. Of course, this problem could be solved by generating a secret key in addition to the request parameters and checking it on the server, but I believe that passing parameters to the url has its limitations. I also want to emphasize a plus in my decision that you can configure the web server so that it will check the presence of thumbnails even without a request to the script.
6. Caching work results
The thumb function simply generates a configuration file if it isn’t already. The name of this file is calculated by the hash of the processing parameters and the name of the image file. The name of the configuration file is the same as the name of the thumbnail plus some extension. For example, if the address "
/static/d1/7e/d17e248758722c42d8c88d21d8b538d7.jpg
" should be a thumbnail, then the configuration file will be called "
/static/d1/7e/d17e248758722c42d8c88d21d8b538d7.jpg.pre
"
Processing is done when the browser sends a second request for a thumbnail.
Settings for nginx:
location /static/ { root /www/public; try_files $uri /thumb.php?file=$uri; }
For Apache:
RewriteCond %{REQUEST_URI} ^(/static/) RewriteCond %{SCRIPT_FILENAME} !-f RewriteRule ^(.*)$ thumb.php?file=$1 [L]
As you can see from the settings, if the file already exists, the web server gives it directly, and if not, then the thumb.php file will be called, which will create a thumbnail and save it to disk
These settings are not perfect, just to show the idea.As a result, we have a simple solution to such a frequent task, I tried to describe all the pitfalls I encountered, if I missed something, I will be grateful for the piecework comments.