分享 [分享] Rails 下实时生成多尺寸图

tiseheaini · 2015年06月29日 · 最后由 louisliu813 回复于 2015年07月07日 · 4299 次阅读

在开发网站时,同一张图片时常需要生成多张不同尺寸的缩略图片。

一般有两种策略生成缩略图,一种在上传图片时,生成所有尺寸的缩略图,另一种是请求指定尺寸时再生成缩略图。

前一种方式会有一些限制,当我们需要一种新的图片尺寸时,需要使用脚本重新生成。而后一种方式就比较灵活。(淘宝也是采用这个方案,只是淘宝使用 ImageMagick。)

网上有一种是借助 lua_nginx module 调用 GraphicsMagick 命令生成缩略图片,不过 nginx 默认发布版本是不包含 lua_nginx 模块,需要重新编译安装,这样太麻烦,实现方式并不灵活。我们需要一种更灵活的方式来实现。

使用 Nginx + API 的方式适用于各种 Web 框架。

在 Nginx location 中将图片格式的请求全部拦截下来处理,检查请求的图片是否存在,存在则设置 expires http 缓存。否则就转向 API 去生成缩略图,并返回新生成的缩略图。

server {
    ## 监听 80 端口
    listen 80;
    ## 项目目录
    root xxx;

    server_name example.com;

    location ~* ^(/pictures/).*\.(jpg|jpeg|png|gif|bmp)$ {
        #如果文件存在,则设置过期时间,关闭访问日志
        if ( -f $request_filename ) {
            expires max;
            access_log off;
        }

        #如果文件不存在,则rewrite到产生图片的脚本文件autoimg.php
        if (!-f $request_filename) {
            rewrite \/(\w*_\w*\.(jpg|jpeg|png|gif|bmp)) /pictures/resize?file=$1;
            expires max;
        }
    }

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_pass http://xxxx;
    }
}

nginx 中最重要的代码就是找到某些目录下的所有需要处理的图片,查看图片是否存在,不存在则 rewrite 到指定的 API 下处理。

#匹配 /pictures 路径下的所有常见图片格式,并处理。
location ~* ^(/pictures/).*\.(jpg|jpeg|png|gif|bmp)$ {
    #如果文件存在,则设置过期时间,关闭访问日志
    if ( -f $request_filename ) {
        expires max;
        access_log off;
    }

    #如果文件不存在,则rewrite到生成缩略图的路由 /pictures/resize
    if (!-f $request_filename) {
        rewrite \/(\w*_\w*\.(jpg|jpeg|png|gif|bmp)) /pictures/resize?file=$1;
        expires max;
    }
}

ruby 部分的代码只是一个示例,调用 API 处理图片,并将处理完的图片返回。

def resize
  #找到图片,使用 MiniMagick 改变图片尺寸和图片质量
  #pic = Picture.find(name)
  #image = MiniMagick::Image.open()
  #image.resize
  #image.quality
  #send_file image_path, type: image.mime_type, disposition: "inline"
end

用云存储多好

#1 楼 @miclle 小流量的应用不需要用云存储,我也是重新造轮子,学习一下内部实现。

加上 CDN 吧~

location ......{
  try_files $uri /pictures/resize?file=$uri;
}

#4 楼 @msg7086 暂时需要使用 rewrite \/(\w*_\w*\.(jpg|jpeg|png|gif|bmp)) 匹配到请求的图片文件名称,这样 rails 才能操作图片。 目前我还不知道 try_files 怎么得到请求的图片文件名称。

我们的图片请求比较多,选择了 lua_nginx,初衷是这种图片请求不涉及数据查询,不需要 rails 服务处理。

@tiseheaini 图片更新后,旧生成图的删除需要考虑,免得产品狗来找你 😂

#6 楼 @louisliu813 的确,如果不涉及到数据查询,可以使用 lua_nginx,不过我们的操作需要使用 rails

#7 楼 @tiseheaini 你们现在的操作是怎样的。

我们这边需要提供的功能大致分两点:

  1. 生成不同大小的单图;
  2. 生成不同大小的合成图 (头像 + 背景图);
需要 登录 后方可回复, 如果你还没有账号请 注册新账号