MongoDB 為 Rails 項目搭建圖片伺服器

來源:互聯網
上載者:User

1 安裝和配置 nginx-gridfs

我們存取圖片的請求首先經過 nginx, 然後再通過 nginx-gridfs 轉到 mongodb, 所以配置 nginx-gridfs 是很重要的一步。

如果伺服器上沒有安裝 nginx, 我們參考 https://github.com/mdirolf/nginx-gridfs#installation 的步驟安裝即可,如果已經安裝好了 nginx, 但是沒有安裝 nginx-gridfs 模組,那麼我們可以只安裝 nginx-gridfs 模組。

1.1 安裝 nginx 和 nginx-gridfs

為簡單起見,我們假設是第一次安裝 nginx, 並且是通過原始碼安裝。

1, 下載 nginx 源碼, 並且解壓縮

wget http://nginx.org/download/nginx-1.10.0.tar.gz
tar -xvf nginx-1.10.0.tar.gz
2, 將 nginx-gridfs 複製到本地

git clone git@github.com:mdirolf/nginx-gridfs.git
cd nginx-gridfs
git checkout v0.8
git submodule init
git submodule update
3, build

cd /path/to/nginx-source
./configure --add-module=/path/to/nginx-gridfs/source/
make
make install
1.2 配置 nginx-gridfs

這一步其實就是在 nginx 的 conf 檔案增加 gridfs 相關的內容。

nginx-gridfs 上的官方配置文檔過於簡單,不適合生產環境。我們接下來的配置會在 nginx conf 檔案裡 增加兩個 server, 第 1 個 server 將監聽 4444 連接埠,這個 server 將直接和 mongodb 互動,第 2 個 server 將監聽 5555 連接埠,這個 server 上面會添加圖片緩衝等配置,並且在緩衝到期後會將請求轉寄 到第 1 個 server, 具體配置內容如下:

$ cat nginx.conf

worker_processes  1;

events {
    worker_connections  1024;
}


http {
    default_type  application/octet-stream;
    sendfile        on;

    keepalive_timeout  65;

    include       mime.types;

    proxy_temp_path   /data/nginx_cache/nginx_temp;
    proxy_cache_path  /data/nginx_cache/nginx_cache  levels=1:2   keys_zone=cache_one:4000m inactive=2d max_size=10g;

    server{
        listen       4444;
        location / {
                        gridfs imgdb root_collection=fs field=filename type=string;
                        mongo 127.0.0.1:27017;
          }
    }
     upstream my_server_pool {
        server 127.0.0.1:4444 weight=1;
    }
    server {
        listen       5555;
        location /upload/ {
            proxy_cache cache_one;
            proxy_cache_valid 200 304 2d;
            proxy_cache_valid 301 302 1m;
            proxy_cache_valid any 1s;
            proxy_cache_key $host$uri$is_args$args;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_pass http://my_server_pool/;
            add_header X-Cache HIT-LT;
            expires       max;
        }
    }
}
這樣我們就完成了 nginx-gridfs 的配置。

這裡要注意 /data/nginx_cache/nginx_temp 的存取權限問題,如果 nginx 的 error log 中出現類似 failed (13: Permission denied) while reading upstream 的錯誤,可以使用 chown 命令將 /data/nginx_cache/nginx_temp 的 owner 修改下。

我們分析下 4444 連接埠也就是和 Mongo GridFS 互動的配置的細節:

server{
        listen       4444;
        location / {
                        gridfs imgdb root_collection=fs field=filename type=string;
                        mongo 127.0.0.1:27017;
          }
    }
gridfs: nginx識別外掛程式的名字;
imgdb: 資料庫名稱;
root_collection: 選擇collection,如root_collection=images, mongod就會去找images.files,預設是fs;
field: 查詢欄位,支援_id, filename, 可省略, 預設是_id, 在這裡我們使用 filename 即圖片的名字去尋找圖片;
type: 解釋field的資料類型,支援objectid, int, string, 可省略, 預設是int, 如果我們 field 用的是 filename, 那麼此處需要設定為 string;
user: 使用者名稱, 可省略;
pass: 密碼, 可省略;
mongo: mongodb 的 host 和 port;
2 啟動 nginx 和 mongodb, 並測試是否能夠正常存取圖片

啟動 nginx:

/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
mongodb 的設定檔如下:

port=27017
dbpath=/data/db
logpath=/var/mongodb/log/mongodb.log
logappend=true
fork=true
啟動 mongodb:

mongod -f /path/to/mongodb.conf
在啟動 nginx 和 mongodb 時會出現一些許可權和目錄不存在的問題,可以使用 chown 和 mkdir -p 解決。上面的配置步驟都是在 mac os 上操作的。

測試上傳圖片:

mongofiles --host localhost --port 27017 --db imgdb --local=/path/to/dog.jpg put my_dog.jpg
通過瀏覽器可以訪問已經上傳的圖片:


3 與 Rails 項目整合

3.1 demo 項目簡介

在寫這個 demo 時,我原本打算參照我所在公司的項目的代碼來完成圖片上傳的功能,但是由於公司使用的 rails 版本比較低,相關配套的 gem 在適配比較新的 rails 版本(這個 demo 用的 rails 版本是 4.2.6)時出現了很多問題, 並且相關的配置過於繁瑣複雜,我決定只使用 mongodb 的官方 ruby driver 來實現圖片上傳的功能。

公司項目中為了實現圖片上傳使用了如下的 gem:

# Gemfile
gem 'carrierwave'
gem 'carrierwave-mongoid'
gem 'mongoid'
只使用 mongodb 的官方 ruby driver: mongo,

# Gemfile
gem 'mongo', '~> 2.2'
在此 demo 中, 圖片上傳的商務程序可以簡單到用一句話說清楚:

應用為圖片產生唯一的名稱,然後將圖片提交給圖片伺服器,如果圖片伺服器儲存圖片成功,則將圖片名稱儲存到 資料庫以供應用將來從圖片伺服器拿圖片。

3.2 demo 設計

當然在實際的應用中還涉及到圖片的壓縮,裁減,變換,加浮水印等商務邏輯, 但是這些商務邏輯應該是和圖片上傳隔離的不應該耦合在一起,從這種角度考慮,我比較討厭 carrierwave 之類的圖片上傳庫把圖片的上傳,裁減,變換等工作都放到一個 Uploader 裡去實現。

作為一名程式員,將自己的想法轉化成直觀的圖形有兩點好處:

方便自己寫代碼

方便別人寫代碼

所以我們仍然對圖片上傳這一看似簡單的功能畫一張圖:


通過分析上面的圖, 我們可以分4個步驟實現圖片上傳的功能:

應用方面,我們需要產生 image params, 比如 image name, image 實體等參數提交給圖片伺服器用戶端;

圖片伺服器用戶端方面, 我們需要寫伺服器的配置資訊, 並串連伺服器;

圖片伺服器用戶端方面, 我們需要將圖片參數提交給伺服器;

應用方面, 我們需要處理圖片伺服器的響應, 如果響應成功我們需要將圖片的名稱等參數記錄下來;

3.3 demo 實現

3.3.1 圖片伺服器用戶端的配置和初始化

先寫設定檔:

# config/mongo.yml

development:
  host: localhost
  port: 27017
  database: imgdb
 
test:
  host: localhost
  port: 27017
  database: imgdb
 
staging:
  host: localhost
  port: 27017
  database: imgdb
 
production:
  host: localhost
  port: 27017
  database: imgdb
初始化用戶端:

# config/initializers/mongo.rb

mc = YAML.load_file(Rails.root.join('config', 'mongo.yml'))[Rails.env]
db_url = "mongodb://#{mc['host']}/#{mc['database']}"
$mongo = Mongo::Client.new(db_url)
這樣我們就擁有了一個可以全域訪問的用戶端: $mongo

3.3.2 編寫圖片上傳服務

圖片上傳的服務邏輯非常簡單: 接受一個 file 參數, 返回 filename 和 content_type

# app/services/upload_file_service.rb

class UploadFileService

  def initialize(file)
    @file = file
  end

  def call
    grid = $mongo.database.fs
    grid.upload_from_stream(filename, @file)
    res = {
      filename: filename,
      content_type: @file.content_type
    }
  end

  private

  def filename
    return @filename if @filename.present?
    ext = File.extname(@file.original_filename)
    @filename = "#{SecureRandom.uuid}#{ext}"
  end

end
使用 UploadFileService 也非常簡單,可以參考相關控制器的代碼:

# app/controllers/avatars_controller.rb

class AvatarsController < ApplicationController

  def create
    @avatar = Avatar.new
    file = params[:avatar][:attachment_file_name]
 
   # 調用圖片上傳服務將圖片上傳到圖片伺服器
   # 並將圖片名字存入到資料庫
    res = UploadFileService.new(file).call
    @avatar.attachment_file_name = res[:filename]
    @avatar.attachment_content_type = res[:content_type]
    if @avatar.save
      redirect_to action: 'index'
    else
      render :new
    end
  end

end

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.