Railsを使って、S3にファイルをアップロードする。
Railsを使って、S3へのファイル直接アップロードを試している。 最終ゴールはAPIとして、クライアントから画像データを受け取って、S3にアップロードすることである。 今回は、第一ステップとして、RailsからS3へのアップロード実現を目指す。
試している工程をメモしておく。
やりたいこと
Railsから画像データをS3にアップロードする。
環境
早速やってみる
やりたいことを満たせそうな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_public
とfog_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として機能するようには、別途、実施します。