Ruby on rails rails媒体文件流通过发送数据或发送文件方法接受字节范围请求

Ruby on rails rails媒体文件流通过发送数据或发送文件方法接受字节范围请求,ruby-on-rails,http-headers,sendfile,x-sendfile,Ruby On Rails,Http Headers,Sendfile,X Sendfile,我有以下问题。声音从公用文件夹中隐藏,因为只有某些用户可以访问声音文件。所以我做了一个特定的方法,它的作用类似于一个健全的url,但首先计算当前用户是否被允许访问这个文件 该文件通过send_data方法发送。问题是,我觉得它工作得很慢,如果它能工作的话。。。我用来播放声音的jplayer插件的开发人员告诉我,我应该能够接受字节范围请求以使其正常工作 如何在rails控制器中通过发送带有send_数据的文件或send_文件来实现这一点 谢谢, Markus我已经能够使用send_file成功地提

我有以下问题。声音从公用文件夹中隐藏,因为只有某些用户可以访问声音文件。所以我做了一个特定的方法,它的作用类似于一个健全的url,但首先计算当前用户是否被允许访问这个文件

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

如何在rails控制器中通过发送带有send_数据的文件或send_文件来实现这一点

谢谢,
Markus

我已经能够使用send_file成功地提供这些文件。虽然我有一个问题,但查找歌曲的早期部分会导致一个新的请求,该请求使歌曲从0:00重新启动,而不是从seekbar的真实位置。到目前为止,我一直在为自己工作:

  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)

这是我的版本。我使用gem'ogginforb'来计算正确提供ogg文件所需的持续时间。 p、 我总是有三种格式——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

另一个修订版本-我试图下载一个zip文件作为二进制内容,这就是我的工作-

def byte_range_响应(请求、响应、内容)
文件\u begin=0
文件大小=content.bytesize
file\u end=文件大小-1
状态\代码='206部分内容'
match=request.headers['range'].match(/bytes=(\d+)-(\d*)/)
如果匹配
文件\u begin=匹配[1]
如果匹配[2]&&,则file_end=match[2]!匹配[2]。是否为空?
结束
content\u length=文件\u end.to\u i-文件\u begin.to\u i+1
response.header['Content-Range']='bytes'+file_begin.to_s+'-'+file_end.to_s+/'+file_size.to_s
response.header['Content-Length']=Content\u Length.to\u s
response.header['Cache-Control']='public,必须重新验证,最大期限=0'
response.header['Pragma']='no cache'
response.header['Accept-Ranges']='bytes'
response.header['Content-Transfer-Encoding']='binary'
发送数据获取部分内容(内容、内容长度、文件开始到),键入:“应用程序/八位字节流”,状态:状态代码
结束
def get_partial_内容(内容、内容长度、偏移量)
test_file=Tempfile.new(['test-file','.zip'])
测试文件.put(内容)
部分内容=IO.binread(测试文件路径、内容长度、偏移量)
test_file.close
test_file.unlink
部分内容
结束

我使用了加勒特的答案并对其进行了修改(包括一到两个bug修复)。我还使用了
send_data
,而不是从文件中读取:

def stream_数据,选项={}
范围\u开始=0
文件大小=data.length
范围\结束=文件\大小-1
状态代码=“200”
if request.headers[“Range”]
状态代码=“206”
request.headers['range'].match(/bytes=(\d+)-(\d*)/)。请尝试进行匹配|
范围\u开始=匹配[1]。到\u i
范围\u end=match[2]。到\u i,除非匹配[2]&为空?
结束
response.header[“Content Range”]=“bytes#{Range_start}-#{Range_end}/#{file_size}”
结束
响应.标题[“内容长度”]=(范围结束-范围开始+1)。至
响应.标题[“接受范围”]=“字节”
发送数据(数据[范围开始,范围结束],
文件名:选项[:文件名],
类型:选项[:类型],
处置:“内联”,
状态:状态(U代码)
结束

您使用的是哪台web服务器?apache 2,您试过了吗?我刚试过这个,它可以正常工作。尽管这里实际上不需要流选项(:stream和:buffer_size)。围绕“Accept Ranges”头的部分正是需要的,以指示NGINX发送字节范围。我们不能依赖内容的持续时间,因为我们不仅发送音频,还发送某些类型的视频。所以我们跳过了这一部分,看起来效果不错。