2015-10-29 61 views
2

我有一个Rails应用程序,用户上传音频文件。我想将它们发送到第三方服务器,并且我需要使用Web套接字连接到外部服务器,所以我需要我的Rails应用程序成为websocket客户端。如何通过Rails的Web套接字发送二进制文件

我想弄清楚如何正确设置。我还没有承诺任何宝石,但'faye-websocket'宝石看起来很有希望。我甚至在“Sending large file in websocket before timeout”中找到了类似的答案,但是,使用该代码对我无效。

这里是我的代码示例:

@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。他们documentation说:

send(message) accepts either a String or an Array of byte-sized integers and sends a text or binary message over the connection to the other peer; binary data must be encoded as an Array.

我不知道如何实现这一目标。如何使用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 vs File.openFile.new

另外,也许这个宝石不是发送二进制数据的最佳选择。有没有人有成功发送带有websockets的Rails二进制文件?

更新:我确实找到了一种方法来使这个工作,但它对记忆是可怕的。对于那些希望别人来加载小文件,你可以简单地File.binreadunpack方法:

但是,如果我用一个单纯的100MB的文件相同的代码,服务器内存用完。它耗尽了我测试服务器上的全部可用1.5GB!有谁知道如何做到这一点是内存安全的方式?

回答

1

这是我对此采取:

# 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 

这是唯一公平的说,我是Iodine's笔者,这是我在Plezi用于写(REST风格的WebSocket实时应用程序框架,你可以使用独立或在内的Rails)...我超级偏向;-)

我会避免gets,因为它的大小可能包括整个文件或单个字节,具体取决于下一个行尾(EOL)标记的位置... read可以更好地控制每个块的大小。

相关问题