从流块写入文件
Write to file from stream block
处理有时需要 return 大文件的 Web 服务,并希望它快速向客户端发送内容,这样客户端就不会在等待数据开始时超时。 stream
似乎很适合这个,但我 运行 遇到了问题。
愚蠢的例子:
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
这很好用:
$ curl http://localhost:4567/path
Hello,
World!
但是我有一个服务写入的辅助日志,并且尝试将文件 I/O 与流 API 混合根本不起作用:
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
现在我明白了:
$ curl http://localhost:4567/path
curl: (18) transfer closed with outstanding read data remaining
Puma 没有指出服务器端有任何问题,但 Thin 完全退出:
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
那么如果我想从流块内部写出一些东西到输出流以外的地方怎么办?
不确定这是否是最佳解决方案,但使用异步 em-files
gem 对我有用,即使在 Puma 中(我知道它不是基于 EventMachine 的):
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
处理有时需要 return 大文件的 Web 服务,并希望它快速向客户端发送内容,这样客户端就不会在等待数据开始时超时。 stream
似乎很适合这个,但我 运行 遇到了问题。
愚蠢的例子:
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
这很好用:
$ curl http://localhost:4567/path
Hello,
World!
但是我有一个服务写入的辅助日志,并且尝试将文件 I/O 与流 API 混合根本不起作用:
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
现在我明白了:
$ curl http://localhost:4567/path
curl: (18) transfer closed with outstanding read data remaining
Puma 没有指出服务器端有任何问题,但 Thin 完全退出:
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
那么如果我想从流块内部写出一些东西到输出流以外的地方怎么办?
不确定这是否是最佳解决方案,但使用异步 em-files
gem 对我有用,即使在 Puma 中(我知道它不是基于 EventMachine 的):
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