Sinatra では Sinatra::Streaming (Sinatra API Documentation) を用いてレスポンスボディをちょっとずつ返す
(Sinatra 1.3.0 & Padrino 0.10.3 がリリースされました。ざっくり紹介(1)。 « blog.udzura.jp) ことができる.
テキストコンテンツではうまく動いた.次の例に web ブラウザから接続すると順次表示されていく.
require 'sinatra/streaming'
helpers Sinatra::Streaming
get '/stream-text' do
stream do |out|
0.upto( 1000 ) do |i|
out << i
out.flush
sleep 1
end
end
end
archive-zip は Create a new archive which will be written to a pipe
することができる.ところがこの stream にはうまく出力できない.次の例では,壊れた zip がダウンロードされる.
require 'rubygems'
require 'archive/zip'
require 'archive/zip/entry'
require 'sinatra/streaming'
helpers Sinatra::Streaming
get '/stream.zip' do
content_type 'application/zip'
stream do |out|
Archive::Zip.open( out, :w ) do |zip|
0.upto( 1000 ) do |i|
zip.add_entry( Archive::Zip::Entry.from_file( $0, :zip_path => "#{i}" ) )
end
end
out.close
end
end
$ zip -T stream.zip zip warning: local and central headers differ for (null) zip warning: offset 10--local = 00, central = 48 zip warning: offset 11--local = 00, central = 8b zip warning: offset 12--local = 00, central = fd zip warning: offset 13--local = 00, central = 01 zip warning: offset 14--local = 00, central = 31 zip warning: offset 15--local = 00, central = 01 zip warning: offset 18--local = 00, central = d7 zip warning: offset 19--local = 00, central = 01 zip error: Zip file structure invalid (stream-direct.zip)
次のように間に IO::pipe を入れるとうまくいく.追及していない.
require 'rubygems'
require 'archive/zip'
require 'archive/zip/entry'
require 'sinatra/streaming'
helpers Sinatra::Streaming
get '/stream-pipe.zip' do
content_type 'application/zip'
stream do |out|
reader, writer = IO.pipe
write_t = Thread.new do
Thread.pass
Archive::Zip.open( writer, :w ) do |zip|
0.upto( 1000 ) do |i|
zip.add_entry( Archive::Zip::Entry.from_file( $0, :zip_path => "#{i}" ) )
end
end
writer.close
end
until reader.eof?
out << reader.readpartial( 4 ) # optimize it
end
end
end
$ zip -T stream-pipe.zip test of stream-pipe.zip OK
(2013-09-21 追記) IO#readpartial
を用いるととても遅い.IO#sysread
も同様.IO#read(十分に大きい値)
ならばずっと速いが,ブロックしてしまうので,あまり大きな値を引数に与えると未完了でも HTTP サーバが接続を切ってしまう.結局,うまい方法は見出せていない.