Nov. 10, 2012 Django Nginx Python RU/EN По-русски

Fast resize and caching of images with "django-nginx-image"

For a project KinsburgTV I had to do resize images. At first I copied from one of my projects a template tag for resizing (taking roots from this snippet), but for production it is very slow solution. So I wrote another template tag, which built correct URL for Nginx, and this URL handled in ngx_http_image_filter_module and proxy_cache.

I have a chart for comparison, which I did through JMeter, but it was not informative for me. If anyone interested, here's the chart. At the left side is PIL, at the right side Nginx. Select for 1000 requests.

Obtained a faster, transparent for applications a resizer and cropper, with caching results. And command for convert images to supported format. So, meet django-nginx-image!

Installation and settings

Install from PyPI:

pip install django-nginx-image

Next, you should setup Nginx:

  • <STORAGE_ROOT> - a path to "media/statiс" directories (example "/storage/kinsburg_tv");
  • <CACHE_NAME> - arbitrarily name for cache (example "kinsburg_tv_thumbnails_cache").
http {

    # Specify a desired path to cache directory, name of cache and maximum size of cache
    proxy_cache_path <STORAGE_ROOT>/nginx/cache levels=1:2 keys_zone=<CACHE_NAME>:10m max_size=1G;

    # Now, you must setup a "server", which will cache results
    server {
        listen 80;
        server_name www.example.org;

        location ~* ^/(resize|crop)/ {
            proxy_pass http://image.example.org$request_uri;
            proxy_cache <CACHE_NAME>;
            proxy_cache_key "$host$document_uri";
            proxy_cache_valid 200 1d;
            proxy_cache_valid any 1m;
            proxy_cache_use_stale error timeout invalid_header updating;
        }
    }

    # And a second "server", which will be resize and crop
    server {
        listen 80;
        server_name image.example.org;

        location ~* ^/resize/([\d\-]+)/([\d\-]+)/(.+)$ {
            alias <STORAGE_ROOT>/$3;
            image_filter resize $1 $2;
            image_filter_buffer 2M;
            error_page 415 = /empty;
        }

        location ~* ^/crop/([\d\-]+)/([\d\-]+)/(.+)$ {
            alias <STORAGE_ROOT>/$3;
            image_filter crop $1 $2;
            image_filter_buffer 2M;
            error_page 415 = /empty;
        }

        location = /empty {
            empty_gif;
        }
    }
}

If you have no need for caching, then simply remove a location from the first "server" and move locations from the second section "server" in the the first. Caching is increased performance to 10-20%.

Then add to settings.py:

INSTALLED_APPS = (
    'nginx_image',
)

Scheme to illustrate the work

scheme to illustrate the work

Usage

In any of your templates, plug in the module nginx_image with the template tag thumbnail, give to tag a path to image , width, height and optional flag crop to crop the image, not resize:

{% load nginx_image %}

Proportionally resize a image, based on the width and the height:
    <img src="{% thumbnail user.profile.avatar 130 130 %}" />

Proportionally resize a image, based on the width:
    <img src="{% thumbnail user.profile.avatar 130 '-' %}" />
    <img src="{% thumbnail user.profile.avatar 130 0 %}" />
    <img src="{% thumbnail user.profile.avatar 130 %}" />

Proportionally resize a image, based on the height:
    <img src="{% thumbnail user.profile.avatar '-' 130 %}" />
    <img src="{% thumbnail user.profile.avatar 0 130 %}" />

Crop a image:
    <img src="{% thumbnail user.profile.avatar 130 130 crop=1 %}" />
    <img src="{% thumbnail user.profile.avatar 130 0 crop=1 %}" />
    <img src="{% thumbnail user.profile.avatar 0 130 crop=1 %}" />

In general, this tag only generates URL like this:

  • /resize/130/-/media/users/avatars/12345.jpg
  • /resize/-/130/media/users/avatars/12345.jpg
  • /crop/130/-/media/users/avatars/12345.jpg

This tag can be used outside a template, for example:

class Film(models.Model):
    thumbnail = models.ImageField(upload_to='films/thumbnails', null=True)

    def thumbnail_preview_url(self):
        from nginx_image.templatetags.nginx_image import thumbnail
        return thumbnail(self.thumbnail, 160, 140, crop=True)

The command nginx_image_converter

Unfortunately (or fortunately), ngx_http_image_filter_module only supports JPEG, GIF and PNG, so I had to write a converter that converts the formats other than the above to JPEG.

./manage.py nginx_image_converter -i /storage/project/media -o /storage/project/newmedia

Additional options for this command you can see here.

The problem with Nginx resolver

If you found a bug in nginx/error.log:

... no resolver defined to resolve image.example.org ...

then setup resolver in nginx/nginx.conf, example:

resolver 127.0.0.1 8.8.8.8;

Comments

Post your comment

Markdown