5

我有一个 Rails 应用程序可以保护上传的视频,将它们放入私人文件夹。

现在我需要播放这些视频,当我在控制器中做这样的事情时:

  def show
    video = Video.find(params[:id])
    send_file(video.full_path, type: "video/mp4", disposition: "inline")
  end

并在 /videos/:id 打开浏览器(Chrome 或 FF),它不会播放视频。

如果我将相同的视频放在公共文件夹中,并像 /video.mp4 一样访问它,它将播放。

如果我删除 dispositon: "inline" 它将下载视频,我可以从我的计算机上播放它。webm 视频也会发生同样的情况。

我错过了什么?这有可能吗?

4

1 回答 1

6

要流式传输视频,我们必须为某些浏览器处理请求的字节范围。

解决方案 1:使用send_file_with_rangegem

简单的方法是使用send_file_with_range gemsend_file修补方法。

在 Gemfile 中包含 gem

# 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

补丁很短,值得一看。但是,不幸的是,它不适用于 Rails 4.2。

send_file解决方案 2:手动修补

受 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: truerange: true我发现这个 railscast 很有帮助:

http://railscasts.com/episodes/266-http-streaming

于 2016-06-01T13:28:48.247 回答