link
- Amazon Web Services -> http://aws.amazon.com/
- Nginx -> http://nginx.com/
- Nginx wiki -> http://wiki.nginx.org/Main
- Imager -> http://search.cpan.org/~tonyc/Imager-0.94/Imager.pm
Dynamic image distribution
receive http request, then return the image.
- look up requested img on local (h80-piyo.jpg),
if it exists, return it でしゅ~りょ~ - if requested img doesn’t exist on local, send request to imageHandler module
- look up base img (piyo.jpg)
if it doesn’t exist, download from S3, then save to local - convert base img (h80-piyo.jpg) -> requested img (h80-piyo.jpg), then save to local
- look up base img (piyo.jpg)
- return requested img (h80-piyo.jpg)
HTTP request
$DOMAIN/$DIR/$BUCKET/$meta_info-$file.$ext?h=$hash
var | desc |
---|---|
DOMAIN | img.hidepiy.comとかね |
DIR | ディレクトリ名 掘る機能はOFFにしてるから、手動で掘ってね☆ |
BUCKET | bucket name of S3 |
meta info | set meta info how to convert image |
file | file name |
ext | file’s extention |
hash | used as easy password |
hash
sha1($secret_key$meta_info-$FILE.$ext)
mode (set in meta_info)
mode | example | hogehoge |
---|---|---|
original | piyo.jpg or o-piyo.jpg | |
screen | sffff00-piyo.jpg | ffff00のように16進数で色指定でスクリーン合成 |
crop | c100x100-piyo.jpg | M100x100でリサイズしたあとに、はみ出たとこcrop |
CROP | C100x100-piyo.jpg | 100*100を切り出す |
min resize | m100x100-piyo.jpg | 縦横比キープ、100*100に画像が納まるようにリサイズ |
MAX resize | M100x100-piyo.jpg | 縦横比キープ、100*100の四角を画像が隠すようにリサイズ |
no propotion | n100x100-piyo.jpg | 縦横比を捨てて100*100にリサイズ |
width | w80-piyo.jpg | 縦横比をキープしつつ、横幅80にリサイズ |
WIDTH | W80-piyo.jpg | 縦はそのまんま横幅80にリサイズ |
height | w80-piyo.jpg | 縦横比をキープしつつ、高さ80にリサイズ |
HEIGHT | W80-piyo.jpg | 横はそのまんま高さ80にリサイズ |
remove | r-piyo.jpg | remove piyo.jpg *-piyo.jpg |
- c100x100-sffff00-piyo.jpg のように、screenは他のモードにくっつけられる
- 100×100 ← こ~ゆ~のは width x height
- 見てのとおり、ファイル名に “-”(ハイフン)は使えない。アンスコとかでもいいかもね
sample
screen
doraemon.jpg(180×240) | sffc0cb-doraemon.jpg | s008000-doraemon.jpg |
---|---|---|
crop
c100x100-doraemon.jpg | C100x100-doraemon.jpg |
---|---|
resize
m100x100-doraemon.jpg | M100x100-doraemon.jpg | n100x100-doraemon.jpg |
---|---|---|
width
w80-doraemon.jpg | W80-doraemon.jpg |
---|---|
height
h80-doraemon.jpg | H80-doraemon.jpg |
---|---|
set-up
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# nginx apt-get install make cd /usr/local/src wget http://nginx.org/download/nginx-1.2.1.tar.gz tar xfvx nginx-1.2.1.tar.gz cd /usr/local/src/nginx-1.2.1 ./configure --prefix=/usr/local/nginx-1.2.1 --with-http_perl_module --with-http_ssl_module --with-http_realip_module make make install /usr/local/nginx-1.2.1/sbin/nginx -V nginx version: nginx/1.2.1 built by gcc 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) TLS SNI support enabled configure arguments: --prefix=/usr/local/nginx-1.2.1 --with-http_perl_module --with-http_ssl_module --with-http_realip_module cd /usr/local/ ln -s nginx-1.2.1 nginx # perl, sha1, Imager apt-get install gcc libpcre3 libpcre3-dev libssl-dev libperl-dev cpan -i Digest::SHA1 apt-get install libjpeg-dev libpng-dev libgif-dev giflib-tools libtiff-dev libfreetype6-dev cpan -i Imager perl -MImager -e 'print join "\n", sort keys %Imager::formats' bmp ft2 gif ifs jpeg png pnm raw tga tiff |
nginx.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#user nobody; worker_processes 1; worker_rlimit_nofile 1024; worker_priority -5; error_log logs/error.log; pid logs/nginx.pid; events { multi_accept on; worker_connections 512; } http { perl_modules /usr/local/lib/perl/5.14.2; perl_require imageHandler.pm; include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; keepalive_timeout 65; server { listen 80; server_name hogehoge; root html/pic; #expires 1d; location / { try_files $uri $uri/ @ih; } location @ih { perl image::handler; } #error_page 404 /404.html; error_page 404 =200 /empty.gif; # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location = /empty.gif { empty_gif; } } } |
imageHandler
perl module, using Imager library inside.
CPAN Imager : http://search.cpan.org/~tonyc/Imager-0.91/Imager.pm
I’ve tried or tried to try these,
Imlib2, ImageMagick, libd, then the Imager is the best for my use.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
package image; use strict; use warnings; use nginx; use Imager; use File::Basename; qw (dirname); use File::Path 'mkpath'; use LWP::Simple; use Digest::SHA1 qw (sha1_hex); our $SECRET_KEY = "doraemon"; our $BASE_DIR = "/usr/local/nginx/html/pic"; our $S3 = "http://s3-ap-northeast-1.amazonaws.com"; our $DEF_MODE = "o"; our $DEF_W = 100; our $DEF_H = 100; our $DEF_C = "original"; our $DEF_Q = 100; our $MAX_W = 1000; our $MAX_H = 1000; our $MAX_SIZE = 3000; sub parse_uri { my ($uri, $args) = @_; my $metaFileExt = basename $uri; my $dirname = dirname $uri; $dirname =~s@^\/@@; my ($bucket) = reverse split('/', $dirname); my ($fileExt, @meta_array) = reverse split('-', $metaFileExt); my ($mode, $width, $height, $quality, $color); if ( defined $meta_array[0] ) { foreach my $meta_tmp (@meta_array) { $mode = substr($meta_tmp, 0, 1, ""); if ("q" eq $mode) { $quality = $meta_tmp; } elsif ("s" eq $mode) { $color = $meta_tmp; } elsif ("w" eq $mode || "W" eq $mode) { $width = $meta_tmp; } elsif ("h" eq $mode || "H" eq $mode) { $height = $meta_tmp; } else { ($width, $height) = split('x', $meta_tmp); } } } my %param_map; if ( defined $args) { %param_map = map { my ($key, $value) = split ('=', $_); $key => $value; } (split '&', $args); } return +{ dirname => $dirname, bucket => $bucket, metaFileExt => $metaFileExt, fileExt => $fileExt, mode => defined $mode ? $mode : $DEF_MODE, width => defined $width ? $width : $DEF_W, height => defined $height ? $height : $DEF_H, quality => defined $quality ? $quality : $DEF_Q, color => defined $color ? $color : $DEF_C, param_h => defined $param_map{"h"} ? $param_map{"h"} : "", }; } sub download { my ($file_from, $file_to) = @_; getstore($file_from, $file_to) or die "Couldn't get it!!!"; } sub check_h { my ($filename, $param_h) = @_; return sha1_hex("$SECRET_KEY$filename") eq $param_h ? 1 : 0; } sub create_image { my ($original_file, $dest_file, $mode, $width, $height, $quality, $color) = @_; my $img = Imager->new; $img->read( file => $original_file) or die $img->errstr; if ("c" eq $mode) { $img = $img->scale(xpixels => $width, ypixels => $height, type => 'max',); $img = $img->crop(width => $width, height => $height,); } elsif ("C" eq $mode) { $img = $img->crop(width => $width, height => $height,); } elsif ("m" eq $mode) { $img = $img->scale(xpixels => $width, ypixels => $height, type => 'min',); } elsif ("M" eq $mode) { $img = $img->scale(xpixels => $width, ypixels => $height, type => 'max',); } elsif ("n" eq $mode) { $img = $img->scale(xpixels => $width, ypixels => $height, type => 'nonprop',); } elsif ("w" eq $mode) { $img = $img->scale(xpixels => $width,); } elsif ("W" eq $mode) { $img = $img->scaleX(pixels => $width,); } elsif ("h" eq $mode) { $img = $img->scale(ypixels => $height,); } elsif ("H" eq $mode) { $img = $img->scaleY(pixels => $height,); } elsif ("s" eq $mode) { } else { return; } if ($DEF_C eq $color) { } else { my $base_color = Imager::Color->new($color) or die $img->errstr(); my ($base_r, $base_g, $base_b) = $base_color->rgba(); my @map_r = map { int($_ + $base_r - ($_ * $base_r / 255) ) } 0..255; my @map_g = map { int($_ + $base_g - ($_ * $base_g / 255) ) } 0..255; my @map_b = map { int($_ + $base_b - ($_ * $base_b / 255) ) } 0..255; $img->map(maps => [\@map_r, \@map_g, \@map_b]); } $img->write(file => $dest_file, jpegquality => $quality,) or die $img->errstr; return; } sub handler { my $r = shift; my $uri = $r->uri; my $args = $r->args; my $file_info = &parse_uri($uri, $args); return DECLINED unless $file_info; my $dirname = $file_info->{dirname}; my $bucket = $file_info->{bucket}; my $metaFileExt = $file_info->{metaFileExt}; my $fileExt = $file_info->{fileExt}; my $mode = $file_info->{mode}; my $width = $file_info->{width}; my $height = $file_info->{height}; my $quality = $file_info->{quality}; my $color = $file_info->{color}; my $param_h = $file_info->{param_h}; my $bucket_file = "$S3/$bucket/$fileExt"; my $original_file = "$BASE_DIR/$dirname/$fileExt"; my $dest_file = "$BASE_DIR/$dirname/$metaFileExt"; return DECLINED if -e $dest_file; if (! -e $original_file) { # mkpath ("$BASE_DIR/$dirname", mode => 0775); &download($bucket_file, $original_file); } if (! -e $original_file) { print FILE "original_file still doesn't exist!!!\n"; } return DECLINED unless -e $original_file; if (!&check_h($metaFileExt, $param_h)) { return DECLINED; } if ($DEF_MODE eq $mode) { return DECLINED; } if ("r" eq $mode) { unlink($original_file); unlink(glob("$BASE_DIR/$dirname/*-$fileExt")); return DECLINED; } if ($width > $MAX_W || $height > $MAX_H) { return DECLINED; } &create_image($original_file, $dest_file, $mode, $width, $height, $quality, $color); return DECLINED; } |
Outroduction