Ruby
Rails
S3
RubyOnRails

【Rails】S3からファイルをダウンロードするやり方

記事を書こうと思った理由

  • ある日、RailsでS3からダウンロードの処理を書こうとした際、「あれ?そういえば、S3からのダウンロードってどうやってやるだっけ?」って調べたところ、S3へのアップロードの記事は多く見つけたけど、S3からダウンロードの記事が少ないと思い、せっかくなら記事にしちゃえ!と思いました。

準備するもの

  • 前提として、S3へのアップロードができるアプリ
    • S3へのアップロードのやり方が書かれてある記事は多くあるので、詳しい設定などは省きます。
  • 開発環境
    • Ruby 2.4.3
    • Ruby on Rails 5.1.4
    • Amazon S3にすでにアップロードされている画像

開発手順

それでは、コードを書いていきます。

1. downloadアクションのルート定義

プライマリーキーが必要になるので、 collection ではなくmember ですね。

routes.rb
resources :shared_files,  only: %i(create destroy) do
  member do
    get :download
  end
end
$ bundle exec rake routes | grep download

下のように、pathが生成されていることを確認したら、次に行きましょう。

download_shared_file GET /shared_files/:id/download(.:format) shared_files#download

2. view

button_to は、 method オプションがデフォルトで post になるので、 しっかりと { method: :get }methodオプションの値を定義しましょう。

shared_files/index.html.slim
.shared-files__file-buttons
    button.shared-files__file-download-button
      = button_to 'ダウンロード', download_shared_file_path(shared_file), { method: :get }

3. モデル

モデルに少々メソッドを定義しましょう。環境変数は適宜定義していただけると幸いです。

shared_file.rb
class SharedFile < ApplicationRecord

  AMAZON_S3_DOMAIN = "https://#{ENV['AWS_HOST_NAME']}"

  mount_uploader :name, SharedFileUploader

  def file_url
    "#{AMAZON_S3_DOMAIN}/#{ENV['AWS_S3_DEV_BUCKET']}/#{self.file_name}"
  end

  def file_name
    self.name.file.filename
  end

  def content_type
    self.name.content_type
  end
end

4.controller

  • いよいよコントローラーの実装に入ります。
  • この data.read の処理を探すのに、結構時間がかかりました。
  • 仕様を読んだり、プロダクトマネージャーに確認をとったところ、日本語がリンクに含まれることから、 URI.encode でエンコードします。
  • ポイントは、 disposition: 'attachment' と、 ファイルの種類を type: オプションで指定すること。disposition キーにはデフォルトでattachment設定されていますが、明記しました。 type でしっかりファイルの種類を指定してあげないと、全部 jpeg としてダウンロードされてしまいます。
  • send_dataのドキュメント ↓
  • http://railsdoc.com/references/send_data
shared_files_controller.rb
class SharedFilesController < ApplicationController
  def index 
    @shared_files = SharedFile.all
  end

  def download
    @shared_file = SharedFile.find(params[:id)
    data = open(URI.encode(@shared_file.file_url))
    send_data data.read, disposition: 'attachment',
              filename: @shared_file.file_name, type: @shared_file.content_type 
  end
end

これでダウンロード処理の完成です!お疲れ様でした。

まとめ

  • 今回のような「あれ?この処理ってどうやって書くんだっけ?」と思うことは、すぐ記事にまとめてアウトプットしていきます!
  • 自分は上のように書きましたが、もっといいやり方があればコメントに残していただけると幸いです。

参考にした記事

https://stackoverflow.com/questions/12277971/using-send-file-to-download-a-file-from-amazon-s3/15028162#15028162