Rails send_file not playing mp4

I have a Rails application that protects downloaded videos by putting them in 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 

And open a browser (Chrome or FF) in / videos /: id that doesn’t play the video.

If I put the same video in a shared folder and access it like /video.mp4, it will play.

If I remove the delimiter: "inline", it will download the video and I can play it from my computer. Something is happening with online videos.

What am I missing? Is it possible to do this?

+4
source share
1 answer

For streaming video, we need to process the requested range of bytes for some browsers.

Solution 1: use send_file_with_range gem

A simple way could be the send_file method, fixed by the send_file_with_range gem .

Include Pearl in Gemfile

 # Gemfile gem 'send_file_with_range' 

and specify the range: true parameter for send_file :

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

The patch is quite short and noteworthy. But unfortunately, this did not work for me with Rails 4.2.

Solution 2: manually send_file patch

Inspired by a gem, manually expanding a controller is quite simple:

 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 

Further reading

Because, firstly, I did not know the difference between stream: true and range: true , I found this railscast useful:

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

+4
source

Source: https://habr.com/ru/post/1444682/


All Articles