利用 Nginx 的 ngx_http_image_filter_module 作實時的圖片縮略圖

你還在用 ImageMagick 生成網站的上傳圖片縮略圖嗎?其實有更好的方法一部到位,簡單有效。php

現而今有很是多的雲存儲服務支持圖片空間,並根據設定的規則來生成空間裏面的圖片縮略圖了,例如 UpYun、Aliyun OSS 都支持。html

但有時候咱們會由於一些其餘的考慮(例如:價格因素),選擇本地文件存儲上傳文件,這個時候,咱們如何實現圖片縮略圖呢?nginx

其實 Nginx 內置了 ngx_http_image_filter_module 能夠幫助你處理圖片:git

  • 縮放
  • 裁剪
  • 調整圖片品質
  • 旋轉
  • 銳化

咱們經常使用的可能就是縮放和裁剪了,根據業務和設計須要,在合適的位置不一樣尺寸的縮略圖。github

安裝

可能一些標準的 Nginx 安裝包沒有帶這個 module 的,你須要使用 Nginx 官方的源安裝,並額外安裝 nginx-module-image-filter 這個包:ubuntu

curl -O http://nginx.org/keys/nginx_signing.key
sudo apt-key add nginx_signing.key
sudo bash -c 'echo "deb http://nginx.org/packages/ubuntu/ $(lsb_release -cs) nginx
deb-src http://nginx.org/packages/ubuntu/ $(lsb_release -cs) nginx" > /etc/apt/sources.list.d/nginx-stable.list'
sudo apt-get update
sudo apt-get install -y nginx nginx-module-image-filter

也能夠直接用作好的 安裝腳本瀏覽器

curl -sSL https://git.io/vVHhf | bash

場景

以 Ruby China 的場景爲例,我設計了下面幾種不一樣的縮略圖版本:緩存

版本名稱 限定尺寸 (px) 縮略方式
large 1920 限定寬度,高度自適應
lg 192x192 固定寬度和高度
md 96x96 固定寬度和高度
sm 48x48 固定寬度和高度
xs 32x32 固定寬度和高度

配置 Nginx

假定咱們的上傳文件存放在 /var/www/homeland/public/uploads 裏面。ruby

下面是 Ruby China 這個縮略圖規則的完整 Nginx 配置:bash

/etc/nginx/nginx.conf

user nobody;
worker_processes auto;
pid /var/www/pids/nginx.pid;
daemon on;

# 載入 ngx_http_image_filter_module
load_module modules/ngx_http_image_filter_module.so;

http {
   # ... 省略
}

/etc/nginx/conf.d/ruby-china.conf

proxy_cache_path /var/www/cache/uploads-thumb levels=1:2 keys_zone=uploads_thumb:10m max_size=50G;

server {
  listen 80 default_server;
  listen 443 ssl http2;

  root /var/www/homeland/public;

  location /uploads {
    expires 7d;
    gzip_static on;

    add_header Cache-Control public;
    add_header X-Pownered "nginx_image_filter";
    # HTTP Response Header 增長 proxy_cache 的命中狀態,以便於之後調試,檢查問題
    add_header X-Cache-Status $upstream_cache_status;

    proxy_pass http://127.0.0.1/_img/uploads;
    # 將縮略圖緩存在服務,避免每次請求都從新生成
    proxy_cache uploads_thumb;
    # 當收到 HTTP Header Pragma: no-cache 的時候,忽略 proxy_cache
    # 此配置能讓瀏覽器強制刷新的時候,忽略 proxy_cache 從新生成縮略圖
    proxy_cache_bypass $http_pragma;
    # 因爲 Upload 文件通常都沒參數的,因此至今用 host + document_uri 做爲
    proxy_cache_key "$host$document_uri";
    # 有效的文件,在服務器緩存 7 天
    proxy_cache_valid 200 7d;
    proxy_cache_use_stale error timeout invalid_header updating;
    proxy_cache_revalidate on;
    # 處理 proxy 的 error
    proxy_intercept_errors on;
    error_page   415 = /assets/415.png;
    error_page   404 = /assets/404.png;
  }

  # 原始圖片
  location /_img/uploads {
    alias /var/www/homeland/public/uploads/$filename;
    expires 7d;
  }

  # 縮略圖
  location ~* /_img/uploads/(.+)!(large|lg|md|sm|xs)$ {
    set $filename /uploads/$1;

    if (-f $filename) {
      break;
    }

    # 根據 URL 地址 ! 後面的圖片版原本準備好須要的參數(寬度、高度、裁剪或縮放)
    set $img_version $2;
    set $img_type resize;
    set $img_w    -;
    set $img_h    -;
    if ($img_version = 'large') {
      set $img_type resize;
      set $img_w    1920;
    }
    if ($img_version = 'lg') {
      set $img_type crop;
      set $img_w    192;
      set $img_h    192;
    }
    if ($img_version = 'md') {
      set $img_type crop;
      set $img_w    96;
      set $img_h    96;
    }
    if ($img_version = 'sm') {
      set $img_type crop;
      set $img_w    48;
      set $img_h    48;
    }
    if ($img_version = 'xs') {
      set $img_type crop;
      set $img_w    32;
      set $img_h    32;
    }
    rewrite ^ /_$img_type;
  }

  # 縮放圖片的處理
  location /_resize {
    alias /var/www/homeland/public$filename;
    image_filter resize $img_w $img_h;
    image_filter_jpeg_quality 95;
    image_filter_buffer         20M;
    image_filter_interlace      on;
  }

  # 裁剪圖片的處理
  location /_crop {
    alias /var/www/homeland/public$filename;
    image_filter crop $img_w $img_h;
    image_filter_jpeg_quality 95;
    image_filter_buffer         20M;
    image_filter_interlace      on;
  }
}

你可能會以爲上面爲什麼寫得這麼繞啊!

沒辦法,Nginx 不支持在 if {} 這個 block 裏面用 image_filter 函數,image_filter 的第一個參數 resize/crop也不能用變量的方式傳輸,因此...

而後,重啓 Nginx,就能夠嘗試了。

注意點

  • 因爲開啓了 proxy_cache 縮略圖將會在服務器上以文件的形式存在,你須要確保每次上傳新文件名儘量的是惟一的(例如用時間,或文件內容 MD5 做爲文件名,參考 CarrieWave 文件名設計
  • 瀏覽器強制刷新,會發起 Pragma: no-cache 的 Request Header,Nginx 會忽略 proxy_cache 從新生成圖片。

效果演示

 

擴展閱讀

相關文章
相關標籤/搜索