rails paperclip aws/s3の構成はよくあるパターンだと思います。
gemのpaperclipを画像だけでなくzipファイルやcsvファイルの配置に利用しました。
そこででっかいファイルをダウンロードするには以下の数パターンあります。
- x-sendfileでnginx,apacheを利用
- s3のurlへredirect
- 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出力を調査してみました。
サンプル
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では以下のように利用したいと思いました。
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
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で有れば以下のように設定したら行けると思います。
ほげほげ
..
.
<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を利用するか、設計から考え直した方が良いかと思います。