我有一个Rails应用程序,用户可以在其中上传音频文件。我想把它们发送到第三方服务器,并且我需要使用Web套接字连接到外部服务器,所以,我需要我的Rails应用程序成为一个Web套接字客户端。
我正在想办法把它设置好。我还没有承诺任何宝石,但"faye websocket"宝石看起来很有前景。我甚至在"超时前在websocket中发送大文件"中找到了类似的答案,然而,使用该代码对我不起作用
下面是我的代码示例:
@message = Array.new
EM.run {
ws = Faye::WebSocket::Client.new("wss://example_url.com")
ws.on :open do |event|
File.open('path/to/audio_file.wav','rb') do |f|
ws.send(f.gets)
end
end
ws.on :message do |event|
@message << [event.data]
end
ws.on :close do |event|
ws = nil
EM.stop
end
}
当我使用它时,我从接收方服务器收到一个错误:
No JSON object could be decoded
这是有道理的,因为我认为它的格式不适合faye websocket。他们的文件上写着:
send(message)接受字符串或字节大小的数组integers,并通过连接将文本或二进制消息发送到其他同行;二进制数据必须编码为数组。
我不知道如何做到这一点。如何使用Ruby将二进制加载到整数数组中?
我尝试修改send
命令以使用bytes
方法:
File.open('path/to/audio_file.wav','rb') do |f|
ws.send(f.gets.bytes)
end
但现在我收到这个错误:
Stream was 19 bytes but needs to be at least 100 bytes
我知道我的文件是286KB,所以这里出了问题。我弄不清楚什么时候使用File.read
、File.open
和File.new
。
此外,也许这个gem不是发送二进制数据的最佳方式。有人成功地用websocket在Rails中发送二进制文件吗?
更新:我确实找到了让它工作的方法,但这对内存来说很糟糕。对于其他想要加载小文件的人,您可以简单地使用File.binread
和unpack
方法:
ws.on :open do |event|
f = File.binread 'path/to/audio_file.wav'
ws.send(f.unpack('C*'))
end
然而,如果我在一个100MB的文件上使用相同的代码,服务器就会耗尽内存。它耗尽了我的测试服务器上所有可用的1.5GB!有人知道如何做到这是一种记忆安全的方式吗?
以下是我对它的看法:
# do only once when initializing Rails:
require 'iodine/client'
Iodine.force_start!
# this sets the callbacks.
# on_message is always required by Iodine.
options = {}
options[:on_message] = Proc.new do |data|
# this will never get called
puts "incoming data ignored? for:n#{data}"
end
options[:on_open] = Proc.new do
# believe it or not - this variable belongs to the websocket connection.
@started_upload = true
# set a task to send the file,
# so the on_open initialization doesn't block incoming messages.
Iodine.run do
# read the file and write to the websocket.
File.open('filename','r') do |f|
buffer = String.new # recycle the String's allocated memory
write f.read(65_536, buffer) until f.eof?
@started_upload = :done
end
# close the connection
close
end
end
options[:on_close] = Proc.new do |data|
# can we notify the user that the file was uploaded?
if @started_upload == :done
# we did it :-)
else
# what happened?
end
end
# will not wait for a connection:
Iodine::Http.ws_connect "wss://example_url.com", options
# OR
# will wait for a connection, raising errors if failed.
Iodine::Http::WebsocketClient.connect "wss://example_url.com", options
值得一提的是,我是碘的作者,我写这篇文章是为了在Plezi(一个RESTful Websocket实时应用程序框架,你可以单独使用或在Rails中使用)中使用。。。我非常有偏见;-)
我会避免使用gets
,因为它的大小可能包括整个文件或单个字节,这取决于下一个行结束(EOL)标记的位置。。。read
使您能够更好地控制每个区块的大小。