Rails send_file不播放mp4
我有一个Rails应用程序,可以保护上传的video,将它们放入私人文件夹。
现在我需要播放这些video,当我在控制器中执行此类操作时:
def show video = Video.find(params[:id]) send_file(video.full_path, type: "video/mp4", disposition: "inline") end
并在/ videos /:打开浏览器(Chrome或FF),但不播放video。
如果我将相同的video放在公共文件夹中,并像/video.mp4一样访问它,它将播放。
如果我删除了dispositon:“inline”它会下载video,我可以从我的电脑上播放它。 webmvideo也是如此。
我错过了什么? 这可能吗?
要流式传输video,我们必须为某些浏览器处理请求的字节范围 。
解决方案1:使用send_file_with_range
gem
简单的方法是让send_file_with_range gem修补send_file
方法。
在Gemfile中包含gem
# Gemfile gem 'send_file_with_range'
并为send_file
提供range: true
选项:
def show video = Video.find(params[:id]) send_file video.full_path, type: "video/mp4", disposition: "inline", range: true end
补丁很短,值得一看。 但是,不幸的是,它对我来说对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
和range: true
之间的区别,我发现这个railscast很有帮助: