t-sanoブログ

メモです。マイペースにアウトプットします。

Railsを使って、S3にファイルをアップロードする。

Railsを使って、S3へのファイル直接アップロードを試している。 最終ゴールはAPIとして、クライアントから画像データを受け取って、S3にアップロードすることである。 今回は、第一ステップとして、RailsからS3へのアップロード実現を目指す。

試している工程をメモしておく。

やりたいこと

Railsから画像データをS3にアップロードする。

環境

Rails 5.0.0(APIモード)

早速やってみる

やりたいことを満たせそうなgemがいくつかありそうだったが、 今回はcarrierwaveを利用する。

ということで、まずは、Gemfileに下記を追加した。

gem 'carrierwave'
gem 'fog-aws'
gem 'rmagick'

carrierwaveは画像ファイルをアップロードに利用するため。 fog-awsはS3に画像ファイルをアップロードするため。 rmagickは画像ファイルの操作に利用するため。

gemをインストールした後は、railsコマンドでアップローダというものを作れるらしい。 下記コマンドでアップローダを生成した。

rails g uploader Image

コマンドを流したら、アップローダができた。 appの下にuploadersというディレクトリができており、配下にアップローダが生成されている。

生成したアップローダはもう利用できるらしい。 あらかじめ作成しているモデルに、アップローダをマウントしてやる。 カラムとアップローダを紐付けるようなイメージ。

class Test < ApplicationRecord
  mount_uploader :image, ImageUploader

これでどうやら、紐付けたカラム(上記例だと、imageカラム)に対して画像を保存できるようだ。 試しにやってみた。

test.image = Rack::Test::UploadedFile.new('/path/to/test_upload.jpeg','image/jpeg')

登録されたみたい。saveしたら、うまく入ってくれた。 saveした中身を見てみると、imageカラムにはファイル名が入っていた。 単純にfindでレコードを見ても、imageカラムには、ファイル名、test_upload.jpegが入っていた。 カラム指定でimageカラムを見ると、アップロードしたファイルの詳細が確認できた。

とりあえず、画像オブジェクトを渡せば、画像の情報をデータベースに格納してくれてるっぽいところは確認できた。 また、public/uploadsというディレクトリができており、アップロードしたファイルはそこに格納されていた。 恐らく、carrierwaveのデフォルト設定で保存先がローカルストレージになっているためだと思う。

しかし! 目的は、S3にアップロードすること!

ということで、S3へアップロードするために、carriewaveの設定を変更する。

まずは、画像の保存先を変更。 initializersにcarrierwave.rbというファイルを作成し、下記のような設定を追加した。

CarrierWave.configure do |config|
  config.fog_provider = 'fog/aws'
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: 'ACCESS_KEY_ID(ENV)',
    aws_secret_access_key: 'ACCESS_KEY(ENV)',
    region: 'ap-northeast-1',
    host: 's3.example.com', ,
    endpoint: 'https://s3.example.com:8080'
  }
  config.cache_storage = :fog

  # S3のURLに直アクセス禁止
  # config.fog_public = false

  # S3のURLに有効期限を60秒で設定する
  # config.fog_authenticated_url_expiration = 60

  # S3バケットを指定
  config.fog_directory  = 'BUCKET_NAME(ENV)'
end

# 日本語入力を可能にするため。
CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/

今回は利用していないが、fog_publicfog_authenticated_url_expirationについて説明する。 fog_publicを利用することで、URLへの直アクセスを制御できる。 デフォルトはtrueらしい。 これがtrueの場合は、アップロードしたオブジェクトのアクセス許可に、 全員ダウンロードができる権限が付与される。 faluseにすると、アクセス許可は設定されず、公開されない。 fog_authenticated_url_expirationを利用することで、URLの有効期限を設定できるみたい。(秒数単位で) 設定した有効期限以降は、画像のURLに直アクセスしても閲覧できなくなる・・・と思っている。

さて、次はアップローダの設定も。 今回は、画像サイズの制御はしないので、シンプルに。

class ImageUploader < CarrierWave::Uploader::Base
  storage :fog

  # 格納するディレクトリを指定
  def store_dir
    "#{model.id}"
  end

  # キャッシュを格納ディレクトリを指定
  def cache_dir
    "cache"
  end
end

この時点でアップロードできるのか試す。 が、S3にバケットの準備を忘れていた。 ので、バケットと作成したバケットにのみアップロードできるIAMユーザを作成する。

で、railsを起動したら怒られた。 initializerでメソッドのnotfoundが発生した。(fog_providerがない。) どうやら、もろもろ、githubからインストールしないと使えないみたいだ。 githubからgemを取得するように変更したら、正常に起動した。

gem 'carrierwave', github: 'carrierwaveuploader/carrierwave'

さて、次こそ試してみる。

・・・

なんか、アップロードされてる! まず、カラムに突っ込んだ始点で、キャッシュが指定したディレクトリ構成でS3に格納されていた。

次に、いよいよ、saveしてみるとどうなるか。 結果、指定したとおりのディレクトリ構成でアップロードされた! カラムも拡張子込みのファイル名になっている。

格納したパスは

カラム名.path

で取得できそう。 他、いろいろ変数を持っているので使えそう。 カラムの中を覗くと、何があるか確認できる。

さて、S3への格納はできた。 しかし、まだAPIとして使えるようにしなければ・・・。 あとは、画像サイズのバリデーション処理とかね。

APIとして機能するようには、別途、実施します。

参考にしたサイト

github.com

paranishian.hateblo.jp