What is the proper way to serve mp4 files through

2019-01-22 09:49发布

问题:

We're having trouble serving mp4s that will play on an ipad using a default rails 3 app. The mp4 is served correctly when viewing the route in chrome and other browsers on a desktop.

Here is our code:

file_path = File.join(Rails.root, 'test.mp4')
send_file(file_path, :disposition => "inline", :type => "video/mp4")

We hit 0.0.0.0:3000/video/test.mp4 to view the video and are presented with cannot play icon on the ipad. We've tried modifying various headers "Content-Length", "Content-Range", etc but they don't seem to affect the end result.

We've also tried using send_data to some extent

i.e.

File.open(file_path, "r") do |f|
    send_data f.read, :type => "video/mp4"
end 

The same video serves fine from the public folder when viewed on the Ipad.

What is the proper way to serve mp4 files through rails to an Ipad?

回答1:

The problem seems to be that rails doesn't handle http-range requests which ios needs for streaming mp4s.

This was our solution for development, (using thin as our server):

  if(request.headers["HTTP_RANGE"]) && Rails.env.development?

    size = File.size(file_path)
    bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
    offset = bytes.begin
    length = bytes.end - bytes.begin + 1

    response.header["Accept-Ranges"]=  "bytes"
    response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}"
    response.header["Content-Length"] = "#{length}"

    send_data IO.binread(file_path,length, offset), :type => "video/mp4", :stream => true,  :disposition => 'inline',
              :file_name => file_name

  else
    send_file(file_path, :disposition => 'inline', :stream => true, :file_name => file_name)
  end

Ultimately we will be using nginx XSendfile to serve the assets in our production environment as the above solution is much slower than what we need.