问题描述
我有一个 Rails 应用程序可以保护上传的视频并将它们放入一个私人文件夹中.
I have a Rails app that protects the uploaded videos putting them into a private folder.
现在我需要播放这些视频,当我在控制器中执行类似操作时:
Now I need to play these videos, and when I do something like this in the controller:
def show
video = Video.find(params[:id])
send_file(video.full_path, type: "video/mp4", disposition: "inline")
end
并在/videos/:id 处打开浏览器(Chrome 或 FF),它不会播放视频.
And open the browser(Chrome or FF) at /videos/:id it doesn't play the video.
如果我将相同的视频放在公共文件夹中,并像/video.mp4 一样访问它,它就会播放.
If I put the same video at the public folder, and access it like /video.mp4 it will play.
如果我删除 dispositon: "inline" 它将下载视频,我可以从我的电脑上播放它.webm 视频也会发生同样的情况.
If I remove the dispositon: "inline" it will download the video and I can play it from my computer. Samething happens with webm videos.
我错过了什么?这有可能吗?
What am I missing? Is this something possible to do?
推荐答案
要流式传输视频,我们必须处理请求的 某些浏览器的字节范围.
To stream videos, we have to handle the requested byte range for some browsers.
简单的方法是让 send_file
方法由 send_file_with_range gem 修补一>.
The easy way would be to have the send_file
method patched by the send_file_with_range gem.
Include the gem in the Gemfile
# Gemfile
gem 'send_file_with_range'
并为 send_file
提供 range: true
选项:
and provide the range: true
option for 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.
The patch is quite short and worth a look.But, unfortunately, it did not work for me with Rails 4.2.
受到 gem 的启发,手动扩展控制器相当容易:
Inspired by the gem, extending the controller manually is fairly easy:
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 很有帮助:
Further reading
Because, at first, I did not know the difference between stream: true
and range: true
, I found this railscast helpful:
http://railscasts.com/episodes/266-http-streaming
这篇关于Rails send_file 不播放 mp4的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!