LoginSignup
32
26

More than 5 years have passed since last update.

s3に配置されている大きなデータをrails経由でストリーミングダウンロードする

Last updated at Posted at 2014-03-07

rails paperclip aws/s3の構成はよくあるパターンだと思います。

gemのpaperclipを画像だけでなくzipファイルやcsvファイルの配置に利用しました。

そこででっかいファイルをダウンロードするには以下の数パターンあります。

  1. x-sendfileでnginx,apacheを利用
  2. s3のurlへredirect
  3. s3のファイルをrails経由でstreamで吐き出す。

1,2の参考
http://thewebfellas.com/blog/2009/8/29/protecting-your-paperclip-downloads
2の参考
https://github.com/thoughtbot/paperclip/wiki/Restricting-Access-to-Objects-Stored-on-Amazon-S3

今回は背後にs3が居ることを悟られたくない、大きいファイルがあることから
1のx-sendfileを選択しましたが、実験的に3のrails経由のstream出力を調査してみました。

サンプル

/app/models/big_file.rb
class BigFile < ActiveRecord::Base
  # paperclip
  has_attached_file :data,
    storage: :s3,
    s3_credentials: "#{Rails.root.to_s}/config/s3.yml",
    s3_permissions: :private,
    url: "/system/:class/:attachment/:id/:filename" #styleは不要
end

controllerでは以下のように利用したいと思いました。

/app/controllers/big_files_controller.rb
def download
  @file = BigFile.find(params[:id])
  # @file.nameは拡張子付きだと仮定 この辺は実装する方にお任せ
  render s3: @file.data, filename: "#{@file.name}"
end

もちろんrender :s3とか存在しないので作成します。
作成時に以下のサイトを参考にしました。
http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/S3Object.html

/config/initializers/s3_renderer.rb
ActionController::Renderers.add :s3 do |obj, options|
  gc_interval = options[:gc_interval] || 500
  filename = options[:filename]

  s3_object = obj.s3_object
  content_type = s3_object.content_type

  self.response.headers["Content-Type"] ||= content_type
  self.response.headers["Content-Disposition"] = "attachment; filename=#{filename}"
  self.response.headers["Content-Transfer-Encoding"] = "binary"
  self.response.headers["Last-Modified"] = Time.now.ctime.to_s

  self.response_body = Enumerator.new do |y|
    i = 0
    obj.s3_object.read do |chunk|
      i += 1
      y << chunk
      GC.start if i % gc_interval == 0
    end
  end
end

ローカル開発環境でwebrickを利用している人はストリーミングダウンロードが行われません。
unicornやpuma当たりを利用して開発時に試してみた方が良いです。

もし本番環境で利用する場合はstreamingができるように設定が必要です。
passengerで有れば以下のように設定したら行けると思います。

/etc/httpd/test.conf
ほげほげ
..
.

<VirtualHost *:80>
   ServerName test.test.com
   DocumentRoot /home/hoge/piyo/current/public
   PassengerBufferResponse off # これが必要
   <Directory /home/hoge/piyo/current/public>
      AllowOverride all
      Options -MultiViews
   </Directory>
</VirtualHost>

使ってみた感じでは、
普通にs3のurlを渡してあげた方が良いです。
何より面倒じゃないです。

セキュリティ上ほかのURLがどうしても知られたくないとか、s3を利用していることを知られたくない場合はx-sendfileを利用するか、設計から考え直した方が良いかと思います。

32
26
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
32
26