के साथ क्रोम में स्ट्रीमिंग एमपी 4 स्ट्रीमिंग मैं अपने जीवन के लिए एचटीएमएलटैग के साथ क्रोम में एमपी 4 स्ट्रीम नहीं कर सकता। अगर मैं public
में फ़ाइल छोड़ देता हूं तो सबकुछ ग्रेवी है और उम्मीद के अनुसार काम करता है। लेकिन अगर मैं इसे send_file
का उपयोग करके सेवा करने की कोशिश करता हूं, तो कल्पना की जा सकती है कि बहुत कुछ गलत हो जाता है। मैं एक रेल ऐप का उपयोग कर रहा हूं जो Video
मॉडल के साथ nginx द्वारा प्रॉक्सी किया गया है जिसमें location
विशेषता है जो डिस्क पर एक पूर्ण पथ है।रेल, nginx और send_file
पहले तो मैंने कोशिश की:
def show
send_file Video.find(params[:id]).location
end
और मुझे यकीन है कि मैं महिमा है कि आधुनिक वेब विकास है में basking कर लिया जाएगा। हा। यह क्रोम और फ़ायरफ़ॉक्स दोनों में निभाता है, लेकिन न तो खोजता है और न ही कोई विचार है कि वीडियो कितना समय है। मैंने प्रतिक्रिया शीर्षकों पर पोक किया और महसूस किया कि Content-Type
को application/octet-stream
के रूप में भेजा जा रहा है और Content-Length
सेट नहीं है। उम्म ... wth?
ठीक है, मुझे लगता है मैं रेल में उन लोगों के लिए सेट कर सकते हैं: फ़ायरफ़ॉक्स में अपेक्षा के अनुरूप
def show
video = Video.find(params[:id])
response.headers['Content-Length'] = File.stat(video.location).size
send_file(video.location, type: 'video/mp4')
end
इस बिंदु सब कुछ पर काफी काम करता है। यह जानता है कि वीडियो कितनी देर तक काम करता है और उम्मीद के अनुसार काम करता है। क्रोम को पता चल जाता है कि वीडियो कितना समय है (टाइमस्टैम्प नहीं दिखाता है, लेकिन बार खोज उचित दिखता है) लेकिन मांग काम नहीं करती है।
जाहिर है क्रोम फ़ायरफ़ॉक्स की तुलना में पिकियर है। यह आवश्यक है कि सर्वर Accept-Ranges
हेडर bytes
के साथ हेडर का जवाब दे और 206
और फ़ाइल के उचित भाग के साथ बाद के अनुरोधों (जो उपयोगकर्ता चाहते हैं) का जवाब दें।
ठीक है, तो मैं here से कुछ कोड उधार और उसके बाद मैं इस किया था:
video = Video.find(params[:id])
file_begin = 0
file_size = File.stat(video.location).size
file_end = file_size - 1
if !request.headers["Range"]
status_code = :ok
else
status_code = :partial_content
match = request.headers['Range'].match(/bytes=(\d+)-(\d*)/)
if match
file_begin = match[1]
file_end = match[2] if match[2] && !match[2].empty?
end
response.header["Content-Range"] = "bytes " + file_begin.to_s + "-" + file_end.to_s + "/" + file_size.to_s
end
response.header["Content-Length"] = (file_end.to_i - file_begin.to_i + 1).to_s
response.header["Accept-Ranges"]= "bytes"
response.header["Content-Transfer-Encoding"] = "binary"
send_file(video.location,
:filename => File.basename(video.location),
:type => 'video/mp4',
:disposition => "inline",
:status => status_code,
:stream => 'true',
:buffer_size => 4096)
अब क्रोम तलाश करने का प्रयास है, लेकिन वीडियो रुक और कभी नहीं जब तक पृष्ठ पुनः लोड फिर से काम करता है जब आप ऐसा करेंगे। अरे। इसलिए मैं कर्ल के साथ चारों ओर खेलने के लिए देखने के लिए क्या हो रहा था फैसला किया है और मैं इस खोज की:
$ कर्ल --header "रेंज: बाइट्स = 200-400" http://localhost:8080/videos/1/001.mp4 ftypisomisomiso2avc1mp41 moovlmvhd @ trak \ TKH
$ कर्ल --header "रेंज: बाइट्स = 1200-1400" http://localhost:8080/videos/1/001.mp4 ftypisomisomiso2avc1mp41 moovlmvhd @ trak \ TKH
कोई फर्क नहीं पड़ता बाइट रेंज अनुरोध, डेटा हमेशा फ़ाइल की शुरुआत से शुरू होता है। बाइट्स की उचित मात्रा लौटा दी जाती है (इस मामले में 201 बाइट्स), लेकिन यह हमेशा फाइल की शुरुआत से होती है। स्पष्ट रूप से nginx Content-Length
शीर्षलेख का सम्मान करता है लेकिन Content-Range
शीर्षलेख को अनदेखा करता है।
मेरे nginx.conf
अछूता डिफ़ॉल्ट है:
user www-data;
worker_processes 4;
pid /run/nginx.pid;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
और मेरे app.conf सुंदर बुनियादी है:
upstream unicorn {
server unix:/tmp/unicorn.app.sock fail_timeout=0;
}
server {
listen 80 default deferred;
root /vagrant/public;
try_files $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header HOST $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 5;
}
सबसे पहले मैं nginx 1.4.x कि Ubuntu 14.04 के साथ आता है की कोशिश की, तो 1.7 की कोशिश की एक पीपीए से .x - एक ही परिणाम। मैंने apache2 भी कोशिश की और बिल्कुल वही परिणाम थे।
मैं दोहराना चाहूंगा कि वीडियो फ़ाइल समस्या नहीं है। अगर मैं इसे public
में छोड़ देता हूं तो nginx उचित माइम प्रकार, शीर्षलेख और क्रोम के लिए ठीक से काम करने के लिए आवश्यक सबकुछ के साथ कार्य करता है।
तो मेरे सवाल का एक दो parter है:
क्यों nginx/अपाचे
send_file
(X-Accel-Redirect
/) के साथ यह सब सामान स्वतः प्रबंधन नहीं करती है जब फ़ाइलpublic
से स्थिर परोसा जाता है जैसे कि यह करता है ? रेल में इस सामान को संभालना इतना पीछे है।मैं वास्तव में nginx (या apache) के साथ send_file का उपयोग कैसे कर सकता हूं ताकि क्रोम खुश रहे और मांग की अनुमति दे सके?
अद्यतन 1
ठीक है, तो मैंने सोचा कि मैं अगर मैं प्रॉक्सी के लिए nginx मिल सकता है तस्वीर से बाहर रेल की जटिलता लेने के लिए और बस को देखने की कोशिश करता हूँ सही ढंग से फ़ाइल करें। इसलिए मैंने एक मृत-साधारण नोडज सर्वर को बढ़ाया:
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {
'X-Accel-Redirect': '/path/to/file.mp4'
});
res.end();
}).listen(3000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:3000/');
और क्रोम क्लैम के रूप में खुश है। =/curl -I
यह भी दिखाता है कि Accept-Ranges: bytes
और Content-Type: video/mp4
nginx स्वचालित रूप से डाला जा रहा है - क्योंकि यह होना चाहिए। ऐसा करने से nginx को रोकने से रेल क्या हो सकता है?
अद्यतन 2
मैं करीब हो रही हो सकता है ...
तो मेरे पास है:
def show
video = Video.find(params[:id])
send_file video.location
end
तो मैं मिलता है:
$ curl -I localhost:8080/videos/1/001.mp4
HTTP/1.1 200 OK
Server: nginx/1.7.9
Date: Sun, 18 Jan 2015 12:06:38 GMT
Content-Type: application/octet-stream
Connection: keep-alive
Status: 200 OK
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Disposition: attachment; filename="001.mp4"
Content-Transfer-Encoding: binary
Cache-Control: private
Set-Cookie: request_method=HEAD; path=/
X-Meta-Request-Version: 0.3.4
X-Request-Id: cd80b6e8-2eaa-4575-8241-d86067527094
X-Runtime: 0.041953
और मेरे ऊपर वर्णित सभी समस्याएं हैं I
लेकिन अगर मैं है:
def show
video = Video.find(params[:id])
response.headers['X-Accel-Redirect'] = video.location
head :ok
end
तो मैं मिलता है:
$ curl -I localhost:8080/videos/1/001.mp4
HTTP/1.1 200 OK
Server: nginx/1.7.9
Date: Sun, 18 Jan 2015 12:06:02 GMT
Content-Type: text/html
Content-Length: 186884698
Last-Modified: Sun, 18 Jan 2015 03:49:30 GMT
Connection: keep-alive
Cache-Control: max-age=0, private, must-revalidate
Set-Cookie: request_method=HEAD; path=/
ETag: "54bb2d4a-b23a25a"
Accept-Ranges: bytes
और सब कुछ पूरी तरह से काम करता है।
लेकिन क्यों? उन्हें बिल्कुल वही करना चाहिए। और क्यों nginx Content-Type
स्वचालित रूप से सेट नहीं करता है जैसे कि यह सरल नोडजे उदाहरण के लिए करता है? मेरे पास config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
सेट है। मैंने इसे उसी परिणाम के साथ application.rb
और development.rb
के बीच आगे और आगे ले जाया है। मुझे लगता है मैंने कभी उल्लेख नहीं किया ... यह रेल 4.2.0 है।
अद्यतन 3
अब मैं अपने गेंडा सर्वर बदल दिया है पोर्ट 3000 पर सुनने के लिए (के बाद से मैं पहले से ही NodeJS उदाहरण के लिए 3000 पर nginx बदल सुनने के लिए)। अब मैं सीधे यूनिकॉर्न से अनुरोध कर सकता हूं (चूंकि यह बंदरगाह पर सुन रहा है और सॉकेट नहीं है) इसलिए मुझे पता चला है कि curl -I
सीधे यूनिकॉर्न शो से पता चलता है कि X-Accel-Redirect
हेडर भेजा गया है और केवल curl
आईएनसीएनआईएनआईएन वास्तव में फ़ाइल भेजता है। यह send_file
जैसा है जो ऐसा नहीं कर रहा है।
'स्थान ~/फ़ाइलें (। *)' स्थान ~/files /(.* होना चाहिए '' –