rails media file stream accept byte range request through send_data or send_file method

后端 未结 4 1644
走了就别回头了
走了就别回头了 2020-11-30 05:20

I have the following problem. Sounds are hidden from the public folder, cause there are only certain Users who should have access to the sound files. So I made a certain met

相关标签:
4条回答
  • 2020-11-30 05:39

    I used Garrett's answer and modified it (including one or two bug fixes). I also used send_data instead of reading from a file:

      def stream_data data, options={}
        range_start = 0
        file_size = data.length
        range_end = file_size - 1
        status_code = "200"
    
        if request.headers["Range"]
          status_code = "206"
          request.headers['range'].match(/bytes=(\d+)-(\d*)/).try do |match|
            range_start = match[1].to_i
            range_end = match[2].to_i unless match[2]&.empty?
          end
          response.header["Content-Range"] = "bytes #{range_start}-#{range_end}/#{file_size}"
        end
    
        response.header["Content-Length"] = (range_end - range_start + 1).to_s
        response.header["Accept-Ranges"] = "bytes"
    
        send_data(data[range_start, range_end],
                  filename: options[:filename],
                  type: options[:type],
                  disposition: "inline",
                  status: status_code)
      end
    
    0 讨论(0)
  • 2020-11-30 05:42

    Another amended version - I was trying to download a zip file as binary content and this is what worked for me -

      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
    
    0 讨论(0)
  • 2020-11-30 05:49

    I've been able to serve up the files with some success using send_file. Although I have one hitch, seeking to an earlier part of the song causes a new request which makes the song restart from 0:00 instead of the true location from the seekbar. This is what I have working for me so far:

      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)
    
    0 讨论(0)
  • 2020-11-30 05:49

    Here is my version. I use gem 'ogginfo-rb' to calculate the duration which is required to serve ogg files properly. p.s. I always have three formats - 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 讨论(0)
提交回复
热议问题