要流式传输视频,我们必须处理请求字节范围 https://en.wikipedia.org/wiki/Byte_serving对于某些浏览器。
解决方案 1:使用send_file_with_range
gem
最简单的方法是send_file
方法修补了send_file_with_range gem https://github.com/adamcooke/send_file_with_range.
将 gem 包含在 Gemfile 中
# Gemfile
gem 'send_file_with_range'
并提供range: true
选项send_file
:
def show
video = Video.find(params[:id])
send_file video.full_path, type: "video/mp4",
disposition: "inline", range: true
end
补丁 https://github.com/adamcooke/send_file_with_range/blob/master/lib/send_file_with_range/controller_extension.rb很短,值得一看。
但不幸的是,它不适用于 Rails 4.2。
解决方案2:打补丁send_file
手动
受 gem 的启发,手动扩展控制器相当简单:
class VideosController < ApplicationController
def show
video = Video.find(params[:id])
send_file video.full_path, type: "video/mp4",
disposition: "inline", range: true
end
private
def send_file(path, options = {})
if options[:range]
send_file_with_range(path, options)
else
super(path, options)
end
end
def send_file_with_range(path, options = {})
if File.exist?(path)
size = File.size(path)
if !request.headers["Range"]
status_code = 200 # 200 OK
offset = 0
length = File.size(path)
else
status_code = 206 # 206 Partial Content
bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
offset = bytes.begin
length = bytes.end - bytes.begin
end
response.header["Accept-Ranges"] = "bytes"
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}" if bytes
send_data IO.binread(path, length, offset), options
else
raise ActionController::MissingFile, "Cannot read file #{path}."
end
end
end
进一步阅读
因为一开始我并不知道两者之间的区别stream: true
and range: true
,我发现这个railscast很有帮助:
http://railscasts.com/episodes/266-http-streaming http://railscasts.com/episodes/266-http-streaming