概要
以下の要件を満たしたい。
- rails
- range付きでsend_fileしたい
- 省メモリ
Railsのsend_fileはどう実装されているのか?
以下のように、rangeに対応していない。
- https://github.com/rails/rails/blob/b13a5cb83ea00d6a3d71320fd276ca21049c2544/actionpack/lib/action_controller/metal/data_streaming.rb#L69
- https://github.com/rails/rails/blob/08a93efab6bdf10ba7afafb6e51f4b7809c97ebc/actionpack/lib/action_dispatch/http/response.rb#L325
メモ: to_pathにrespondさせることで、RackのSendFile middlewareのX-SendFileの処理にひっかかるようにしている。
メモ: send_dataの実装
https://github.com/rails/rails/blob/b13a5cb83ea00d6a3d71320fd276ca21049c2544/actionpack/lib/action_controller/metal/data_streaming.rb#L109
Railsのsend_fileをrangeに対応させた既存の実装
以下のような実装だが、ファイルをメモリ上に読み込むので、省メモリではない。
実際、俺はこの実装を使ってメモリ肥大化に苦しんだ。
Rackのsend_file(相当の機能)はどう実装されているのか?
以下のように、rangeに対応しつつ、少しずつ読み込んで返す実装らしい。
簡単な方法
rangeで切り出したファイルを別のファイルとして保存し、それをRailsのsend_fileで送信する。
ディスクも節約したい場合は使いづらい。
また、send_file直後に一時ファイルを削除するかんたんな方法もない(railsでafter response sent的なワードでぐぐってもそれらしいものが出てこない)。
Tempfileを使えばGC時に削除するのは可能。
-> Rack::TempfileReaperを発見 https://github.com/rack/rack/pull/671/files
これを使うと、send_file終了後にtempfileを削除できる
tempfile = Tempfile.new
request.env['rack.tempfiles'] << tempfile
とすれば良い
かっこいい方法
実装方針
TODO
参考1: https://gist.github.com/mudai/9415701
参考2:
include ActionController::Live
response.stream.write
response.stream.close
参考3: Rack::TempfileReaper