- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我这辈子都不能用 html5 将 mp4 流式传输到 Chrome <video>
标签。如果我将文件放在 public
那么一切都是肉汁,并按预期工作。但是如果我尝试使用 send_file
提供服务,几乎所有可以想象的都会出错。我正在使用由 nginx 代理的 rails 应用程序,带有 Video
具有 location
的模型属性是磁盘上的绝对路径。
起初我试过:
def show
send_file Video.find(params[:id]).location
end
Content-Type
正在发送为
application/octet-stream
并且没有
Content-Length
放。嗯……什么?
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
响应后续请求(发生在用户搜索时)以及文件的适当部分。
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)
$ curl --header "Range: bytes=200-400" http://localhost:8080/videos/1/001.mp4 ftypisomisomiso2avc1mp41 �moovlmvhd��@��trak\tkh��
$ curl --header "Range: bytes=1200-1400" http://localhost:8080/videos/1/001.mp4 ftypisomisomiso2avc1mp41 �moovlmvhd��@��trak\tkh��
Content-Length
header 但忽略
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/*;
}
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;
}
public
然后 nginx 为它提供适当的 mime 类型、标题和 Chrome 正常工作所需的一切。
send_file
自动处理所有这些东西( X-Accel-Redirect
/X-Sendfile
) 就像从 public
静态提供文件时一样?在 Rails 中处理这些东西太落后了。 Update 1
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 自动插入 - 应该是这样。 rails 会做什么阻止 nginx 这样做?
Update 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
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
Content-Type
像简单的 nodejs 示例一样自动在这里?我有
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
放。我在
application.rb
之间来回移动了它和
development.rb
结果相同。我想我从来没有提到过……这是 Rails 4.2.0。
Update 3
curl -I
直接给 unicorn 显示没有
X-Accel-Redirect
头被发送,只是
curl
ing unicorn 直接实际发送文件。就像
send_file
没有做它应该做的。
最佳答案
我终于有了最初问题的答案。我没想到我会来到这里。我所有的研究都导致了死胡同、骇人听闻的非解决方案和“它只是开箱即用”(好吧,不适合我)。
Why doesn't nginx/apache handle all this stuff automagically with send_file (X-Accel-Redirect/X-Sendfile) like it does when the file is served statically from public? Handling this stuff in rails is so backwards.
How the heck can I actually use send_file with nginx (or apache) so that Chrome will be happy and allow seeking?
Rack::Sendfile
的评论中找到答案的地方。 .它们被组织成文档,您可以在
rubydoc 上找到这些文档。 .
Rack::Sendfile
要求前端代理发送
X-Sendfile-Type
标题。在 nginx 的情况下,它还需要一个
X-Accel-Mapping
标题。该文档也有 apache 和 lighttpd 的示例。
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_set_header X-Sendfile-Type X-Accel-Redirect; # ADDITION
proxy_set_header X-Accel-Mapping /=/; # ADDITION
proxy_redirect off;
proxy_pass http://localhost:3000;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 5;
}
def show
send_file(Video.find(params[:id]).location)
end
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 ~ /files(.*) { # NEW
internal; # NEW
alias $1; # NEW
} # NEW
location @unicorn {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header HOST $http_host;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping /=/files/; # CHANGED
proxy_redirect off;
proxy_pass http://localhost:3000;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 5;
}
Mime::Type.register('video/mp4', :mp4)
至
config/initializers/mime_types.rb
因此该文件以正确的 mime 类型提供。
关于ruby-on-rails - 使用 rails、nginx 和 send_file 在 Chrome 中流式传输 mp4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28008564/
我想从服务器端向客户端发送一个图像文件。我正在使用 flask 框架。 但问题是每当我调用 send_file() 所在的路由时,响应返回的是一个文件。当我单击此文件时,gedit 打开它时该文件中没
我有一个 Perl Dancer Web 应用程序,它使用 GD 动态创建图像。我正在尝试将这些图像作为 PNG 提供给用户。例如: package MyApp; use Dancer ':synta
在我的项目中,我使用 Carrierwave 上传文件,如 doc 和 pdf . 如果我想下载 pdf 我的 Controller 做 send_file @document, :dispositi
我正在寻找一种下载 xml 文件的方法。我用: file_path = 'folder/' + xml_name + '.xml' send_file file_path, :type => "tex
我用flask编写了一个jQuery,点击它应该执行SQL搜索并将数据帧导出为excel,脚本是: $(function () { $('a#export_to_excel').bind
我有一个 Flask 应用程序,其中服务器使用一个 Flask route 创建一个 csv 文件并将其保存到服务器。使用客户端页面上生成的按钮,触发另一个 Flask route 来获取最新文件,将
有没有办法阻止 Flask 在 send_file 中设置缓存 header ,或者我必须在之后手动操作它们? 最佳答案 send_file 接受 cache_timeout 参数 - 只需将其设置为
我正在使用带有 send_file() 的 Flask 让人们从服务器下载文件。 我目前的代码如下: @app.route('/', methods=["GET", "POST"]) def inde
我正在使用 Flask 应用程序更新一些 PDF 文件,将它们转换为 Excel 文件并将该文件发送回用户。我正在使用实例文件夹来存储 pdf 和 excel 文件。 但是,当用户按下“下载”按钮以下
我正在编写一个 Flask 应用程序,其中有一个 HTTP 端点,它返回我在函数中创建的图像的 send_file。不幸的是,浏览器似乎缓存了该图像,甚至没有再次调用端点,而是在后续重新加载时加载原始
我正在使用 flask 的 send_file尝试让浏览器下载 .txt 文件的方法。问题是浏览器没有下载任何东西。 这是我的 python 函数: @app.route('/download_zip
我正在尝试读取用户上传的图像,然后将图像显示给他们。我想在不保存上传的图像文件的情况下执行此操作。 我有这样的代码: from flask import Flask, redirect, render
我有一个小的 flask 应用程序,它可以上传一些图像并将它们转换成多页 tiff。没什么特别的。 但是如何测试多个文件的上传和文件下载呢? 我的测试客户端: class RestTestCase(u
我目前正在开发一个服务器端 json 接口(interface),其中有几个临时文件在请求期间被操作。 我当前在请求结束时清理这些文件的解决方案如下所示: @app.route("/method",m
我正在尝试使用 Sinatra 的内置 send_file命令,但它似乎不适用于临时文件。 我基本上执行以下操作来压缩 mp3 专辑: get '/example' do songs = ...
我在 Sinatra 应用程序上使用 send_file: get '/update/dl/:upd' do filename ="/uploads/#{params[:upd]}"
我需要为 Apple 通用链接发送具有特定内容类型 (application/pkcs7-mime) 的文件。我使用 Rails 中的 send_file 函数,并添加 :type 作为参数,但类型始
调用 send_file 时,它会将文件发送到浏览器,但浏览器会将内容作为纯文本转储到新页面上,而不是下载文件。如果我刷新该页面,它会正常下载文件。 路线 get 'download' => 'q
我不知道如何通过 send_file 设置内容长度。 我检查了 api,没有内容长度参数。 最佳答案 您可以为响应设置 header ,例如: def download @file = Attac
如何测试发送文件的 Controller 操作? 如果我使用 controller.should_receive(:send_file) 执行此操作,测试会因“缺少模板”而失败,因为没有渲染任何内容。
我是一名优秀的程序员,十分优秀!