поток медиа файлов rails принимает запрос диапазона байтов с помощью метода send_data или send_file - PullRequest
9 голосов
/ 20 июля 2011

У меня следующая проблема.Звуки скрыты в общей папке, потому что только определенные пользователи должны иметь доступ к звуковым файлам.Поэтому я создал определенный метод, который действует как звуковой URL, но сначала рассчитывает, разрешен ли текущему пользователю доступ к этому файлу.

Файл отправляется методом send_data.Проблема только в том, что он работает довольно медленно, если он работает даже ... Разработчик плагина jplayer, который я использую для воспроизведения звука, сказал мне, что я должен иметь возможность принимать запросы диапазона байтов, чтобы он работал правильно...

Как я могу сделать это в контроллере rails, отправив файл с send_data или send_file?

Спасибо, Маркус

Ответы [ 3 ]

9 голосов
/ 30 сентября 2011

Я смог успешно обработать файлы с помощью 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 голосов
/ 17 октября 2013

Вот моя версия.Я использую gem 'ogginfo-rb', чтобы вычислить длительность, необходимую для правильной работы файлов ogg.ps У меня всегда есть три формата - 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 голосов
/ 28 апреля 2015

Еще одна исправленная версия - я пытался загрузить zip-файл в виде двоичного содержимого, и это то, что у меня сработало -

  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
...