2011-07-20 23 views
8

我遇到以下问题。声音对公共文件夹是隐藏的,因为只有某些用户可以访问声音文件。所以我做了一个特定的方法,就像一个声音的URL一样,但首先计算当前用户是否被允许访问这个文件。rails media file stream通过send_data或send_file方法接受字节范围请求

该文件通过send_data方法发送。问题在于,如果它工作得很慢,我的工作就会很慢......用来播放声音的jplayer插件的开发人员告诉我,我应该能够接受字节范围请求以使其正常工作...

如何在rails控制器中通过send_data或send_file发送文件来做到这一点?

感谢, 马库斯

+0

其Web服务器您使用的? – NARKOZ

+0

apache 2与乘客模块 – Markus

+0

你有没有试过https://gist.github.com/mikhailov/3174601? – Anatoly

回答

8

我已经能够以服务的文件将使用由send_file一些成功。尽管我有一个困难,但寻找歌曲的较早部分会导致一个新的请求,使歌曲从0:00开始重新启动,而不是从搜索栏中返回真实位置。这是我目前为我工作的内容:

file_begin = 0 
    file_size = @media.file_file_size 
    file_end = file_size - 1 

    if !request.headers["Range"] 
    status_code = "200 OK" 
    else 
    status_code = "206 Partial Content" 
    match = request.headers['range'].match(/bytes=(\d+)-(\d*)/) 
    if match 
     file_begin = match[1] 
     file_end = match[1] if match[2] && !match[2].empty? 
    end 
    response.header["Content-Range"] = "bytes " + file_begin.to_s + "-" + file_end.to_s + "/" + file_size.to_s 
    end 
    response.header["Content-Length"] = (file_end.to_i - file_begin.to_i + 1).to_s 
    response.header["Last-Modified"] = @media.file_updated_at.to_s 

    response.header["Cache-Control"] = "public, must-revalidate, max-age=0" 
    response.header["Pragma"] = "no-cache" 
    response.header["Accept-Ranges"]= "bytes" 
    response.header["Content-Transfer-Encoding"] = "binary" 
    send_file(DataAccess.getUserMusicDirectory(current_user.public_token) + @media.sub_path, 
      :filename => @media.file_file_name, 
      :type => @media.file_content_type, 
      :disposition => "inline", 
      :status => status_code, 
      :stream => 'true', 
      :buffer_size => 4096) 
+2

我刚试过这个,它工作。尽管在这里实际上并不需要流选项(:stream和:buffer_size)。 –

1

这是我的版本。我使用gem'ogginfo-rb'来计算正确提供ogg文件所需的持续时间。 p.s.我总是有三种格式 - wav,mp3,ogg。

the_file = File.open(file_path) 

file_begin = 0 
file_size = the_file.size 
file_end = file_size - 1 

if request.headers['Range'] 
    status_code = :partial_content 

    match = request.headers['range'].match(/bytes=(\d+)-(\d*)/) 

    if match 
    file_begin = match[1] 
    file_end = match[1] if match[2] and not match[2].empty? 
    end 

    response.headers['Content-Range'] = "bytes #{file_begin}-#{file_end.to_i + (match[2] == '1' ? 1 : 0)}/#{file_size}" 
else 
    status_code = :ok 
end 

response.headers['Content-Length'] = (file_end.to_i - file_begin.to_i + 1).to_s 
response.headers['Last-Modified'] = the_file.mtime 

response.headers['Cache-Control'] = 'public, must-revalidate, max-age=0' 
response.headers['Pragma'] = 'no-cache' 
response.headers['Accept-Ranges'] = 'bytes' 
response.headers['Content-Transfer-Encoding'] = 'binary' 

require 'ogginfo-rb' 
ogginfo = Ogg::Info::open(the_file.path.gsub(/.mp3|.wav/,'.ogg')) 
duration = ogginfo.duration.to_f 

response.headers['Content-Duration'] = duration 
response.headers['X-Content-Duration'] = duration 

send_file file_path, 
      filename: "#{call.id}.#{ext}", 
      type: Mime::Type.lookup_by_extension(ext), 
      status: status_code, 
      disposition: 'inline', 
      stream: 'true', 
      buffer_size: 32768 
+0

'Accept-Ranges'头部周围的部分正是需要的部分,以便测试NGINX发送字节范围。我们不能依赖Content-Duration,因为我们不仅发送音频,还发送一些类型的视频。所以我们跳过了这部分,这似乎工作得很好。 –

0

另一个修正版 - 我试图下载一个压缩文件的二进制内容,这是对我工作 -

def byte_range_response (request, response, content) 
    file_begin = 0 
    file_size = content.bytesize 
    file_end = file_size - 1 

    status_code = '206 Partial Content' 
    match = request.headers['range'].match(/bytes=(\d+)-(\d*)/) 
    if match 
     file_begin = match[1] 
     file_end = match[2] if match[2] && !match[2].empty? 
    end 
    content_length = file_end.to_i - file_begin.to_i + 1 
    response.header['Content-Range'] = 'bytes ' + file_begin.to_s + '-' + file_end.to_s + '/' + file_size.to_s 
    response.header['Content-Length'] = content_length.to_s 
    response.header['Cache-Control'] = 'public, must-revalidate, max-age=0' 
    response.header['Pragma'] = 'no-cache' 
    response.header['Accept-Ranges']= 'bytes' 
    response.header['Content-Transfer-Encoding'] = 'binary' 
    send_data get_partial_content(content, content_length, file_begin.to_i), type: 'application/octet-stream', status: status_code 
    end 

    def get_partial_content(content, content_length, offset) 
    test_file = Tempfile.new(['test-file', '.zip']) 
    test_file.puts(content) 
    partial_content = IO.binread(test_file.path, content_length, offset) 
    test_file.close 
    test_file.unlink 
    partial_content 
    end