はじめに
ホビープロジェクトでモバイル向けのシンプルなTODOアプリを作ることになりました。TODOといえばWebアプリサンプルのド定番だけど、本当に自分にあった使えるものを作ってきっちり公開まではやろうと思ってます。それは置いておいて、サーバサイドをRailsで書きました。
TODOには写真が添付できると良い、というのが要件にあったので、Active Storageを使うことにします。せっかくRails 5.2を使っているので!
Active Storageとは
最高のガイドがあるので基本的にこれを読めばいいです。
ただ、調べてもS3にやってみた、という例が多く、今回は気が向いてGoogle Cloud Storageに保存することにしたのでそちらを記事に残しておこうかな、というのが本記事の主旨です。
ちなみに気が向いた理由ですが、アプリの認証をGoogleでやっていて、すでにプロジェクトがあったからです。
Active Storageを使う準備
参考になりました。ありがとうございます。【Rails 5.2】 Active Storageの使い方 - Qiita
Gemfileに必要なドライバをインストールしておきましょう。
gem "google-cloud-storage", "~> 1.3", require: false
bundle
そしてActive Storage用のテーブルを生成するためのmigrationを行う必要があります。これは内部的に使われているようで、実際は触らずに使えます。
$ rails active_storage:install $ rails db:migrate
画像を持たせるmodelにhas_one_attached :image
を追加。今回はTask modelに持たせました。key名はimageでもphotoでもお好きなものを。
app/models/task.rb
class Task < ApplicationRecord belongs_to :user belongs_to :tag has_one_attached :image # (skip) end
デフォルトでconfig/storage.ymlはdiskになっているので、development環境でこの時点でファイルのアップロードができます。templateはerbでやりました。form_forで受けて画像を添付したいリソースのcreate actionを行うときに、file fieldでファイルをアップロードして、先ほどmodelで入力したkey名をparamにのせてあげます。これだけでできます。すごい。
<%= form_for @task,:url => {:action => :create} do |f| %> <p> <%= f.label :name %> <%= f.text_field :name %> </p> <%= f.file_field :image %> <%= f.submit %>
(root)/storage以下にファイルがアップロードされるでしょう。config/storage.ymlにすでにこのように書いてあるからですね。
5 local: 6 service: Disk 7 root: <%= Rails.root.join("storage") %>
Google Cloud Storageを使う準備
さて、Rails guideを見るにconfig/storage.ymlに以下を埋める必要がありそうです。
google: service: GCS keyfile: { type: "service_account", project_id: "", private_key_id: "", private_key: "", client_email: "", client_id: "", auth_uri: "https://accounts.google.com/o/oauth2/auth", token_uri: "https://accounts.google.com/o/oauth2/token", auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs", client_x509_cert_url: "" } project: "" bucket: ""
service_accountを作ってproject作ってバケット作ればいけそうな雰囲気がしますね。
Service Account作る
プロジェクトは適当に作ってある前提で、サービスアカウントはIAMと管理->サービスアカウント
ユニークな名前をいれましょう。
作成されたら権限を付与します。閲覧だけでは足りないので編集権限をつけます。
Active Storageがアクセスするためのkeyを作成します。
作成するとダウンロードされます。
APIを有効にする
Google Cloud Storage JSON API
を有効にしてください。しないとアクセス時に弾かれます。あと、有効になるまで数分かかります。
ちなみに認証情報を作成する、とありますが多分不要です。この目的に適したサービスアカウントがすでに存在します、と言われるので。
バケットに権限を与える
バケットは普通にぼちぽち作っていただいて、バケットに権限を与えないと弾かれます。サービスアカウントが常に権限もってるじゃん、って思うんですけどね。
ストレージ->ブラウザでバケットの右側の3点ボックスから権限を編集
で、ここがびっくりなんですが、ストレージ管理者の権限が必要です。強すぎでは、、、と思うんですが、サービスアカウント実態にも確かにこれ相当のものを持たせてるから仕方ないかなと無理やり納得しています。
Storage.ymlにダウンロードしたkey情報を反映
なんと、ダウンロードしたkeyを見ると中身はそのままstorage.ymlに載せるべき内容がのっています。このままいれればいいですね。
しかし、中身にはprivate_keyが入っており、localで動かすならまだしもコミットはできないので適切に秘匿しなければなりません。
Encrypted Credential
参考になりました。ありがとうございます。secrets.ymlや環境変数をRails 5.2のEncrypted Credentialsに移行する - Qiita
editorで開きます。
$ EDITOR=vim bin/rails credentials:edit
1 # aws: 2 # access_key_id: 123 3 # secret_access_key: 345 4 5 # Used as the base secret for all MessageVerifiers in Rails, including the one protecting co okies. 6 secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 7 8 private_key_id: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 9 private_key: xxxxxxxxxxxxxxxxxxxxxxxxx
こんな感じで追加してあげます。config/master.keyは秘密鍵になるので絶対に漏らさない/なくさないようにしましょう。(.gitignoreで無視されるようになっています)
最終的なconfig/storage.ymlはこんな感じになります。
google: service: GCS keyfile: { type: "service_account", project_id: "simple-todo-project", private_key_id: <%= Rails.application.credentials.private_key_id %>, private_key: <%= Rails.application.credentials.private_key %>, client_email: "active-storage-sample@simple-todo-project.iam.gserviceaccount.com", client_id: "1111111111111111111111", auth_uri: "https://accounts.google.com/o/oauth2/auth", token_uri: "https://oauth2.googleapis.com/token", auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs", client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/active-storage-sample%40simple-todo-project.iam.gserviceaccount.com" } project: "simple-todo-project" bucket: "simple-todo-project"
生成されたconfig/credentials.yml.enc
はコミットしても大丈夫です。
最後にdevelopmentでもgoogle cloud storageを使うように変更しましょう。
config/environments/development.rb
- config.active_storage.service = :local + config.active_storage.service = :google
おわりに
Active Storage、model側にカラムを追加することなく画像を持たせることができてとても便利だと思いました。ちゃんとリソースを削除すれば画像も削除されます。
GCPのプロジェクト、サービスアカウント、APIに使い慣れてる人ならわかるとは思いますが、はじめて使うひとの参考になれば幸いです。