RailsでHerokuに画像をアップロードする方法|Active Storageの設定ガイド

はじめに

Railsで開発したWebアプリケーションをHerokuにデプロイするとき、画像アップロードの仕組みをどう構築すればよいか悩むことはないでしょうか。
Herokuには、サーバーのファイルシステムが一時的にしか使えないという制約があります。
そのため、画像やファイルをアップロードする際は、クラウドストレージを活用するのが一般的です。

RailsにはActive Storageという仕組みがあり、クラウドストレージと連携して画像をスムーズに保存できます。
この記事では、Active StorageでHeroku上のRailsアプリに画像アップロード機能を導入する基本的なやり方を紹介します。
初心者でもわかりやすいように具体例を交えながら、実務でもよく用いられる方法をまとめます。

この記事を読むとわかること

  • Railsにおける画像アップロードの基本的な仕組み
  • Herokuで画像を保存する際の注意点
  • Active Storageを使った具体的な設定手順
  • Amazon S3などのクラウドストレージとの連携方法
  • よくあるエラーやトラブルシューティングのポイント

Railsでの画像アップロードの概要

Railsにおける画像アップロードの方法

Railsアプリケーションで画像アップロードを実現する方法は複数あります。
代表的なものは以下のとおりです。

  • Active Storage
  • CarrierWave / Shrine などの外部ライブラリ
  • 独自にファイルパスを管理する方法

この中で、Railsに標準で搭載されているのがActive Storageです。
あまり余分な設定を必要とせず、Railsプロジェクトにすぐ組み込みやすい点が魅力です。

Herokuにおけるファイルシステムの特性

Herokuでは、ファイルをサーバー上に保存しても、デプロイのたびに消えてしまう可能性があります。
また、動作中に書き込んだファイルがいつまでも残る保証はありません。
この仕組みを「エフェメラルファイルシステム」と呼ぶこともあります。

Herokuで安定して画像を使うためには、クラウドストレージや外部サービスにアップロードし、そちらで保管するのが一般的です。
Active Storageを使えば、S3などのオブジェクトストレージと連携し、Herokuのエフェメラルファイルシステムに依存しない設計にできます。

Active Storageのメリット

Active Storageを採用するメリットは、Rails標準の機能として用意されているため、以下のような点が挙げられます。

  • 追加のライブラリ選定で迷う必要があまりない
  • Railsのバージョンアップとの互換性が保たれやすい
  • 画像アップロードやダウンロードなどの操作がシンプル
  • 必要に応じて複数のクラウドストレージを切り替えられる

Active Storageを導入することで、画像を含むファイルの保存先を外部のサービス(Amazon S3、Google Cloud Storageなど)に簡単に設定できます。

Herokuで画像アップロードをする際の注意点

ローカルストレージの問題点

Herokuは、短時間であればローカルファイルの書き込みも可能です。
ただし、次のようなシーンでファイルが消失する可能性があります。

  1. アプリケーションを再デプロイしたとき
  2. Dynoのスケールや再起動が発生したとき

初心者の方が最初にハマりやすいポイントは、「開発中はローカルに画像を保存できるが、本番のHeroku環境で画像が突然消えてしまう」という事態です。
これを避けるためには、Heroku環境では外部ストレージを使用する設計が必須になります。

ストレージサービスの選択

Heroku上でのファイル管理には、AWS S3Cloudinaryなどがよく利用されています。
Active Storageを使えば、標準でS3のほか複数のサービスに対応できます。
いずれのストレージを選ぶにしても、環境変数にキーやシークレットを設定し、Rails側でそれを読み込む構成にするのが安全で一般的なやり方です。

Herokuのエフェメラルファイルシステムを想定せずに開発を進めると、リリースしてから「画像が消えた」という予期せぬトラブルに直面することがあります。あらかじめクラウドストレージの導入を検討しておきましょう。

コスト面の考慮

大きな規模のアプリケーションや大量のユーザーがいる場合、ストレージへの通信回数や保存量が増大します。
その結果、ストレージの利用料金がかさむ可能性があります。
本格的な運用をする際は、必要に応じて課金体系を確認し、使用量に応じたモニタリングを行ってください。

Active Storageの基本設定手順

Active Storageをインストールする

RailsプロジェクトでActive Storageを使うには、まずインストールが必要です。
すでにRailsプロジェクトを作成済みであれば、以下のようなコマンドを実行すると、Active Storage用のマイグレーションファイルが生成されます。

bin/rails active_storage:install
bin/rails db:migrate

この操作によって、Railsのデータベースにactive_storage_blobsactive_storage_attachmentsというテーブルが追加されます。
これらは、アップロードされたファイルのメタデータやアプリケーション上の関連付けを管理するために利用されます。

storage.ymlの設定

Active Storageでは、アップロード先をconfig/storage.ymlで管理します。
デフォルトでいくつかの環境向けの設定が用意されていますが、Herokuを使用する場合は外部ストレージを設定する必要があります。

以下は、Amazon S3を使用する例です。
config/storage.yml の中で、amazon というキーに設定を追記します。

amazon:
  service: S3
  access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
  region: <%= ENV["AWS_REGION"] %>
  bucket: <%= ENV["AWS_BUCKET_NAME"] %>

これでRailsは、amazon という設定名を使ったときに、AWSのS3にファイルをアップロードします。

環境ごとの設定ファイル

Railsでは、config/environments/production.rbなどのファイルで、使用するストレージを指定します。
たとえば、本番環境でS3を使う場合は、次のように追記できます。

# config/environments/production.rb
config.active_storage.service = :amazon

ローカル開発用のconfig/environments/development.rbでは、ローカルディスクを使用することも可能です。
ただし、Herokuにデプロイする際は必ず外部ストレージを利用する設定になっているかを確認しましょう。

Herokuでの環境変数設定

Heroku CLIでの設定

AWSのアクセスキーやシークレットキーなどは、Herokuに直接書き込まず、環境変数を使って安全に管理します。
環境変数を設定するには、ローカルのターミナルで以下のようにコマンドを実行できます。

heroku config:set AWS_ACCESS_KEY_ID=xxxx AWS_SECRET_ACCESS_KEY=yyyy AWS_REGION=ap-northeast-1 AWS_BUCKET_NAME=zzz

ここで指定した値は、Railsの <%= ENV["AWS_ACCESS_KEY_ID"] %> のような形で読み込まれます。
config/storage.yml でこれらの値を参照することで、Heroku上でも同じ設定を利用できます。

ダッシュボードでの設定

HerokuのWebダッシュボードからも、環境変数を設定することができます。
対象のアプリケーションを選択し、"Settings" タブを開いて "Reveal Config Vars" をクリックすると、キーとバリューを入力できます。
これもCLIの場合と同様に、Railsアプリから ENV["KEY名"] で読み込むことが可能です。

セキュリティ面への配慮

アクセスキーなどの機密情報は、ソースコードに直接書かないようにします。
Gitリポジトリにキーをプッシュしてしまうと、万が一外部にリポジトリが公開された場合、大きなリスクとなるからです。

アップロード機能の実装手順

モデルとコントローラのセットアップ

画像をアップロードしたいモデルに、Active Storageで管理される添付ファイルを定義します。
例として、ブログの投稿を管理するPostモデルに画像を一つアップロードする場合は、モデルに次のように記述します。

# app/models/post.rb
class Post < ApplicationRecord
  has_one_attached :image
end

もし複数の画像をアップロードしたい場合は、has_many_attached :images のように書きます。

コントローラでは、ストロングパラメータにimageを許可します。
例を挙げると、以下のように設定します。

# app/controllers/posts_controller.rb
def post_params
  params.require(:post).permit(:title, :content, :image)
end

ビューでのフォーム

次に、投稿フォームのビューで、file_fieldを使った画像の入力項目を追加します。
例として、app/views/posts/_form.html.erbに以下のような記述をします。

<%= form_with model: @post, local: true do |form| %>
  <div>
    <%= form.label :title %>
    <%= form.text_field :title %>
  </div>

  <div>
    <%= form.label :content %>
    <%= form.text_area :content %>
  </div>

  <div>
    <%= form.label :image %>
    <%= form.file_field :image %>
  </div>

  <%= form.submit "Create Post" %>
<% end %>

has_many_attached :images を使う場合は、:imageの部分を:imagesに変更し、複数ファイルを選択できるようにすることも可能です。

アップロード画像の表示

アップロードされた画像は、@post.image のようにしてアクセスできます。
ビューで表示するときは、image_tagヘルパーを使います。

<% if @post.image.attached? %>
  <%= image_tag @post.image, alt: "投稿画像" %>
<% end %>

これだけで、Heroku上でも画像が正しく表示されます。
実際のファイル自体はS3にアップロードされるため、Herokuのエフェメラルファイルシステムに影響されることなく画像が保持されます。

実務での活用例

プロフィール画像の管理

ユーザー登録機能を実装したアプリケーションでは、プロフィール画像の設定を求められることが多いです。
Active Storageを使えば、ユーザーモデルにhas_one_attached :avatarを設定し、あとはフォームで画像をアップロードして表示するだけで完結できます。

商品画像のアップロード

ECサイトやマーケットプレイスのような仕組みをRailsで構築する場合、商品画像をアップロードする場面が必ず出てきます。
これもActive Storageなら、画像のアップロード処理や表示処理をまとめて扱えるので、開発がシンプルになります。

画像サイズやサムネイル生成

Active Storageは、代表的な画像加工用ライブラリと連携して、画像のリサイズやサムネイル生成を行えます。
たとえば、必要に応じて別サイズの画像を生成したり、正方形にトリミングしたりすることも容易に実装できます。

複雑な画像加工をする場合は、イメージ処理用のgemを併用し、Active Storageとの連携を行うケースがあります。開発の要件に合わせて拡張してみましょう。

デプロイと動作確認の流れ

Herokuへのデプロイ手順

  1. Gitリポジトリにコードをコミットし、Herokuのリモートを設定します。
  2. heroku create でアプリを作成していない場合は作成します。
  3. git push heroku main (またはmaster) などでHerokuにデプロイします。
  4. 本番環境用のデータベースマイグレーションを実行する場合は heroku run rails db:migrate を行います。

画像アップロード確認

デプロイが完了したら、アプリにアクセスしてフォームから画像をアップロードしてみます。
画像アップロードが完了したら、表示されるURLがHerokuのドメインではなく、S3などのストレージのURLになっていることを確認しましょう。

ログやコンソールを活用する

画像アップロードにトラブルがある場合は、Herokuのログを調べることが大切です。
heroku logs --tail を実行すると、エラーメッセージや警告などをリアルタイムで確認できます。
また、heroku run rails console でHeroku上のRailsコンソールを起動し、実際にPost.last.image.attached?などを試してアップロードの有無を確認できます。

よくあるエラーと対処法

資格情報が不足している

AWSのアクセスキーやシークレットキーが正しく設定されていないと、CredentialMissingErrorのようなメッセージが出ることがあります。
環境変数が正しく設定されているか、スペルミスがないかを確認しましょう。

バケット名が誤っている

S3のバケット名が間違っている、もしくはすでに使用されている名前を指定している場合、Forbiddenエラーなどが発生することがあります。
バケット名を再度確認し、設定ファイルの記述と一致しているかをチェックしてください。

ファイルサイズ制限

S3やCloudinaryなどでは、無料プランやプランごとにアップロードできるファイルのサイズ上限が設定されています。
大きなファイルをアップロードしようとしたときにエラーが起きる場合は、プランの上限を超えていないか確認するとよいでしょう。

CORSエラー

画像をプレビューする機能や、JavaScript経由で画像をアップロードする機能を導入している場合、CORS(クロスオリジンリソース共有)の設定が必要となる場合があります。
S3などの管理コンソールでCORSの設定が無効になっていないか確認します。

複数画像アップロードの実装

モデルの定義

複数の画像をアップロードしたいときは、モデルで次のように定義します。

class Product < ApplicationRecord
  has_many_attached :photos
end

コントローラの記述

ストロングパラメータで :photos を配列として扱うために、以下のように設定します。

def product_params
  params.require(:product).permit(:name, :description, photos: [])
end

:photos => [] の部分が、複数ファイルを受け取るためのポイントです。

ビューでのフォーム

フォーム側も複数ファイルを選択できるようにするなら、file_fieldmultiple: true を指定します。

<%= form_with model: @product, local: true do |form| %>
  <div>
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div>
    <%= form.label :description %>
    <%= form.text_area :description %>
  </div>

  <div>
    <%= form.label :photos %>
    <%= form.file_field :photos, multiple: true %>
  </div>

  <%= form.submit "Create Product" %>
<% end %>

これで、複数のファイルを一度にアップロードできます。

公開範囲のコントロール

パブリックアクセスとプライベートアクセス

Active Storageでは、S3にアップロードするファイルを一時的に署名付きURLでアクセスさせることができます。
パブリックに閲覧可能なURLを直接返す方法もあれば、一時的に有効な署名URLを使って認可されたユーザーだけに表示させる方法もあります。
セキュリティやユーザー体験に応じて、どちらを採用するか検討してください。

ダウンロードリンクの生成

ファイルを直接ダウンロードできるリンクも、Railsのヘルパーメソッドで簡単に作れます。
例として、<%= link_to "Download", rails_blob_path(@post.image, disposition: "attachment") %> と書けば、ユーザーがクリックしたときにファイルがダウンロードされるリンクを表示できます。

ディレクトリ構造のイメージ

最終的に、Railsアプリのディレクトリはおおむね以下のようになります。
Active Storage関連のファイルとしては、主にconfig/storage.ymlと、環境ごとの設定ファイル、マイグレーションファイルが重要です。

my_rails_app/
├── app/
│   ├── controllers/
│   ├── models/
│   ├── views/
│   └── ...
├── config/
│   ├── environments/
│   │   ├── development.rb
│   │   ├── production.rb
│   │   └── test.rb
│   ├── storage.yml
│   └── ...
├── db/
│   ├── migrate/
│   │   ├── 20220101000000_create_active_storage_tables.rb
│   │   └── ...
│   └── schema.rb
├── Gemfile
├── Gemfile.lock
├── Rakefile
└── ...

運用上のチェックポイント

アップロード数や容量のモニタリング

運用が進むにつれて、画像ファイルの数や容量が増大します。
ストレージの利用状況を定期的にチェックし、上限を超えそうになったらプランを変更する、あるいは不要な画像を整理するといった運用が必要です。

リージョンの選択

S3を使うときは、regionをどうするか検討します。
ユーザーが多い地域に近いリージョンを選ぶことで、アップロードやダウンロードの速度向上が期待できます。

SSL対応

多くの場合、S3のエンドポイントはHTTPSでアクセスすることが一般的です。
RailsやHerokuの設定でforce_ssl = trueなどを使っている場合でも、ストレージのURLがHTTPSになっているかを確認し、ユーザーのブラウザ上で警告が出ないように気を配りましょう。

まとめ

HerokuでRailsアプリに画像アップロード機能を実装する場合、Active Storageとクラウドストレージの組み合わせが最も基本的なやり方です。
Herokuのファイルシステムが一時的であるため、外部ストレージを正しく設定しなければ、アップロードした画像が消えてしまいます。

Active StorageはRailsに標準搭載されているため、セットアップの手間が比較的少なく、学習コストも抑えやすいです。
storage.ymlでストレージ先を定義し、Heroku上の環境変数に認証情報を登録する形を取れば、セキュリティ面でも安心して運用できます。

これから画像アップロード機能を設計する方や、運用中にトラブルを抱えている方は、ぜひこの記事を参考にActive Storageを使ってみてはいかがでしょうか。
適切に設定すれば、Herokuでも安定して画像が扱えるアプリを構築できるはずです。

Ruby on Railsをマスターしよう

この記事で学んだRuby on Railsの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。