こんにちは、tahara です。
以前 nginx を使ったサムネイル画像の動的生成 でサムネイル画像の動的生成について書きました。 その後 Paperclip の画像保存先をファイルシステムから S3 に変更しましたので、 あらためて CloudFront + nginx + S3 でのサムネイル画像の動的生成について書きたいと思います。
今回も 簡単!リアルタイム画像変換をNginxだけで行う方法 | cloudrop をおおいに参照させていただきました(感謝です)。
Paperclip の設定です。 CloudFront を使うにあたり次のような感じにします。
- s3_host_name に CloudFront のホスト名
- url に ":s3_alias_url"
- s3_protocol に "" (http でも https でもいけるように)
# Paperclip config.paperclip_defaults = { storage: :s3, s3_credentials: { access_key_id: "XXXXXXXXXXXXXXXXXXXX", secret_access_key: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" }, s3_host_name: "s3-ap-northeast-1.amazonaws.com", bucket: "your.backet.name", path: "/system/:class/:attachment/:id/:style.:extension", s3_host_alias: "xxxxxx.cloudfront.net", # CloudFront のホスト名 url: ":s3_alias_url", s3_protocol: "", # empty string to generate scheme-less URLs :default_style => :normal, :default_url => "/assets/missing_:class_:attachment_:style.png", :auto_orient => true }
config/initializers/paperclip.rb で nginx の HttpImageFilterModule を使って crop, resize するためのメソッドを書きます。 前回と違い S3 に非公開で保存した画像を参照するための expiring_url でも crop, resize するメソッドを追加しています。
module Paperclip module ClassMethods # nginx の HttpImageFilterModule で crop, resize するメソッド class Attachment def crop(width, height, quality=75) ret = "#{url}&w=#{width}&h=#{height}" if quality != 75 ret += "&q=#{quality}" end ret end def resize(width, height, quality=75) crop(width, height, quality) + "&t=r" end def expiring_crop(width, height, quality: 75, time: 1800) ret = "#{expiring_url(time)}&w=#{width}&h=#{height}" if quality != 75 ret += "&q=#{quality}" end ret.sub(/^.*?\?/, url.sub(/\?.*$/, '?')) end def expiring_resize(width, height, quality: 75, time: 1800) expiring_crop(width, height, quality: quality, time: time) + "&t=r" end end end
nginx の設定では次に注意です。
- S3 への proxy_pass のために
- resolver 172.16.0.23;
- ブラウザからのリクエストに Basic 認証の Authorization ヘッダが付いていると S3 に蹴られるので
- proxy_set_header Authorization "";
- CloudFront 経由で expiring_url を使った時 S3 に蹴られるので
- proxy_set_header X-Amz-Cf-Id "";
- proxy_pass での image_filter のために
- image_filter_buffer 5m;
nginx.conf はこんな感じです(関係のあるとこだけ抜粋)。
http { # S3 への proxy_pass のために必要。172.16.0.23 は AWS での DNS みたい resolver 172.16.0.23; server { location ~* ^/system/.*\.(jpg|jpeg|jpe|png|gif)$ { if ($query_string ~ [?&][wh]=.*) { rewrite ^/(.*)$ /image_filter/$1 last; } rewrite ^/(.*)$ /s3_original/$1 last; } location ~* ^/system/.*$ { rewrite ^/(.*)$ /s3_original/$1 last; } location ~* ^/s3_original/(.*)$ { internal; expires 1y; set $file $1; proxy_set_header Authorization ""; proxy_set_header X-Amz-Cf-Id ""; proxy_pass http://s3-ap-northeast-1.amazonaws.com/example.com/$file$is_args$args; } location ~* ^/image_filter/(.*)$ { internal; set $file $1; set $width 150; set $height 150; set $quality 75; if ($arg_w ~ (\d*)) { set $width $1; } if ($arg_h ~ (\d*)) { set $height $1; } if ($arg_q ~ (100|[1-9][0-9]|[1-9])) { set $quality $1; } if ($arg_t = "r") { rewrite ^ /resize last; } rewrite ^ /crop last; } location /resize { internal; expires 1y; proxy_set_header Authorization ""; proxy_set_header X-Amz-Cf-Id ""; proxy_pass http://s3-ap-northeast-1.amazonaws.com/example.com/$file$is_args$args; image_filter_buffer 5m; image_filter resize $width $height; image_filter_jpeg_quality $quality; error_page 415 = @empty; } location /crop { internal; expires 1y; proxy_set_header Authorization ""; proxy_set_header X-Amz-Cf-Id ""; proxy_pass http://s3-ap-northeast-1.amazonaws.com/example.com/$file$is_args$args; image_filter_buffer 5m; image_filter crop $width $height; image_filter_jpeg_quality $quality; error_page 415 = @empty; } location @empty { empty_gif; } } }
なかなか proxy_set_header X-Amz-Cf-Id "";
にたどり着けず泣きそうになりましたが、
これで画像アップし放題、リサイズし放題です!
もう一つ注意すべきことが、 model.photo.exists? を使うとその都度 S3 に HEAD リクエストを投げます。 model.photo.present? を使いましょう。 さもないと、レスポンスタイムがひどいことになります。
最後に、ひき続きエンジニアを募集していますので、よろしくお願いします!