LoginSignup
28
24

More than 5 years have passed since last update.

paperclipとS3を連携して会員限定のダウンロード機能を作る

Last updated at Posted at 2015-02-25

前提

  • 会員限定のダウンロード機能を作りたい。
  • S3は非公開設定になっている。(つまり下記のようにS3にリダイレクトさせるような形は、ダウンロードリンクを共有されるで採用しない)

redirect_to @department.annual_report.url

課題

paperclipを使いPDFをS3にアップした際に、アップしたPDFのダウンロードができなかった。

[Date]  INFO -- : Completed 500 Internal Server Error in 33ms
[Date] FATAL -- :
ActionController::MissingFile (Cannot read file /path/sample.pdf):
  app/controllers/departments_controller.rb:xx:in `annual_report'

コードはこんな感じだった。

def download
  unless user_signed_in?
    redirect_to @department
  else
    send_file @department.annual_report.path, type: @department.annual_report.content_type
  end
end

解決方法

Restricting Access to Objects Stored on Amazon S3
上記リンクに書いてるように expiring_url を指定することで、:s3_permissions => :private と指定されたS3に対して、指定時間内限定でアクセスできる一時的なURLを発行することができる。

def download
  redirect_to @department.annual_report.expiring_url(1.minute)
end

上記コードのようにとても手軽に実装できる。
上の例だと1分間だけ認証が解除された固有のURLが生成される。
ただリダイレクトさせてしまうとアクセス先がS3になってしまい、且つアクセス可能時間を過ぎたら下記のようなエラー画面が出るので、サービスの選択肢としてはない。

S3_log.png

send_dataと組み合わせる

よって上記URLをsend_dataで返すようにした。
これにより、ダウンロードボタンをクリックする度に指定時間の間アクセス可能なURLが発行され、そのURLに存在するファイルをバイナリで読み込み、クライアントに返すということができるようになった。

def download
  unless user_signed_in?
    redirect_to @department
  else
    download_url = @department.annual_report.expiring_url(1.minute)
    file_name    = @department.annual_report_file_name
    content_type = @department.annual_report.content_type

    open(download_url, 'rb') do |data|
      send_data data.read, filename: file_name, type: content_type
    end
  end
end
  1. ログイン判定。
  2. expiring_url(1.minute) で1分間だけアクセス可能なURLを生成。(file_nameはダウンロード時のファイル名)
  3. open-uriでURLをバイナリで読み込み、send_dataでクライアントに返す。

参考

28
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
24