简体   繁体   中英

Write to file from stream block

Working on a web service that sometimes needs to return large files, and want it to send something to the client quickly so the client doesn't time out waiting for the start of the data. stream seemed perfect for this, but I ran into a problem.

Dumb example:

get '/path' do
  status 200
  headers 'Content-Type' => 'text/plain'
  stream do |out|
    sleep 1
    out << "Hello,\n"
    sleep 1
    out << "World!\n"
  end
end

This works fine:

$  curl http://localhost:4567/path
   Hello,
   World!

But I have a side log that the service writes to, and trying to mix File I/O with the streaming API doesn't work at all:

get '/path' do
  status 200
  headers 'Content-Type' => 'text/plain'
  File.open '/tmp/side-log', 'a' do |lf|
    stream do |out|
      lf.puts "Woo!"
      sleep 1
      out << "Hello,\n"
      sleep 1
      out << "World!\n"
    end
  end
end

Now I get this:

$ curl http://localhost:4567/path
curl: (18) transfer closed with outstanding read data remaining

Puma doesn't indicate any problems on the server side, but Thin exits entirely:

hello2.rb:13:in `write': closed stream (IOError)
        from hello2.rb:13:in `puts'
        from hello2.rb:13:in `block (3 levels) in <main>'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:437:in `block (2 levels) in stream'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:628:in `with_params'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:437:in `block in stream'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:403:in `call'
        from vendor/bundle/gems/sinatra-1.4.6/lib/sinatra/base.rb:403:in `block in each'
        from vendor/bundle/gems/eventmachine-1.0.8/lib/eventmachine.rb:1062:in `call'
        from vendor/bundle/gems/eventmachine-1.0.8/lib/eventmachine.rb:1062:in `block in spawn_threadpool'
[1]+  Exit 1                  ruby hello2.rb

So what should I do if I want to write something out to someplace other than the output stream from inside the stream block?

Not sure if this is the best solution, but using the asynchronous em-files gem worked for me, even in Puma (which I understand is not EventMachine-based):

require 'em-files'  

get '/path' do
  status 200
  headers 'Content-Type' => 'text/plain'
  EM::File.open '/tmp/side-log', 'a' do |lf|
    stream do |out|
      lf.write "Woo!\n"
      sleep 1
      out << "Hello,\n"
      sleep 1
      out << "World!\n"
   end
  end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM