使用 phoenix 服务 HTTP 范围请求?
Serving HTTP range request with phoenix?
我目前正在开发一个带有 phoenix 的网站,并且有一个应该在后台播放的视频部分。
虽然它在 Chrome 和 Firefox 上运行良好,但在 Safari 上不起作用。
我怀疑是因为 cowboy 没有正确处理 HTTP 范围请求。
有没有办法启用(如果默认禁用)?
$ curl -H Range:bytes=16- -I http://localhost:4000/videos/vid_home_1.mp4
HTTP/1.1 200 OK
server: Cowboy
date: Tue, 12 Apr 2016 14:41:20 GMT
content-length: 633787
cache-control: public
etag: 480A03F
content-type: video/mp4
当 nginx 服务器显示为 206 时:
$ curl -H Range:bytes=16- -I http://localhost/videos/vid_home_1.mp4
HTTP/1.1 206 Partial Content
Server: nginx/1.8.0
Date: Tue, 12 Apr 2016 14:46:17 GMT
Content-Type: video/mp4
Content-Length: 633771
Last-Modified: Mon, 11 Apr 2016 12:26:26 GMT
Connection: keep-alive
ETag: "570b97f2-9abbb"
Content-Range: bytes 16-633786/633787
Cowboy 似乎(还)不支持 Range
header,因此您需要为此使用不同的网络服务器。
我找到了一种使用 Plugs 自己完成的方法...
所以如果有人想用 Phoenix / Elixir 服务范围请求这是你必须做的(这是非常基本的并且没有考虑 rfc)
defmodule Plug.Range do
@behaviour Plug
@allowed_methods ~w(GET HEAD)
import Plug.Conn
def init(options) do
options
end
def call(conn, _opts) do
if (Enum.empty?(Plug.Conn.get_req_header(conn, "range"))) do
conn
else
file_path = "priv/static" <> conn.request_path
if File.exists? file_path do
stats = File.stat! file_path
filesize = stats.size
req = Regex.run(~r/bytes=([0-9]+)-([0-9]+)?/, conn |> Plug.Conn.get_req_header("range") |> List.first)
{req_start, _} = req |> Enum.at(1) |> Integer.parse
{req_end, _} = req |> Enum.at(2, filesize |> to_string) |> Integer.parse
file_end = ( filesize - 2) |> to_string
length = req_end - req_start + 1
conn
|> Plug.Conn.put_resp_header("Content-Type", "video/mp4")
|> Plug.Conn.put_resp_header("Accept-Ranges", "bytes")
|> Plug.Conn.put_resp_header("Content-Range", "bytes #{req_start}-#{req_end}/#{filesize}")
|> Plug.Conn.send_file(206, file_path, req_start, length)
|> Plug.Conn.halt
else
conn
end
end
end
end
如您所见,现在它只会发送 "video/mp4" content-Type,但您可以轻松地使某些东西适用于所有内容...
最后,为了让插件起作用,您需要将其放在项目端点文件中的 Plug.static 之前。
希望对某人有所帮助...
编辑:
对于那些感兴趣的人,我为此创建了一个 github/hex.pm 包:
Hex link
github link
我目前正在开发一个带有 phoenix 的网站,并且有一个应该在后台播放的视频部分。
虽然它在 Chrome 和 Firefox 上运行良好,但在 Safari 上不起作用。
我怀疑是因为 cowboy 没有正确处理 HTTP 范围请求。
有没有办法启用(如果默认禁用)?
$ curl -H Range:bytes=16- -I http://localhost:4000/videos/vid_home_1.mp4
HTTP/1.1 200 OK
server: Cowboy
date: Tue, 12 Apr 2016 14:41:20 GMT
content-length: 633787
cache-control: public
etag: 480A03F
content-type: video/mp4
当 nginx 服务器显示为 206 时:
$ curl -H Range:bytes=16- -I http://localhost/videos/vid_home_1.mp4
HTTP/1.1 206 Partial Content
Server: nginx/1.8.0
Date: Tue, 12 Apr 2016 14:46:17 GMT
Content-Type: video/mp4
Content-Length: 633771
Last-Modified: Mon, 11 Apr 2016 12:26:26 GMT
Connection: keep-alive
ETag: "570b97f2-9abbb"
Content-Range: bytes 16-633786/633787
Cowboy 似乎(还)不支持 Range
header,因此您需要为此使用不同的网络服务器。
我找到了一种使用 Plugs 自己完成的方法... 所以如果有人想用 Phoenix / Elixir 服务范围请求这是你必须做的(这是非常基本的并且没有考虑 rfc)
defmodule Plug.Range do
@behaviour Plug
@allowed_methods ~w(GET HEAD)
import Plug.Conn
def init(options) do
options
end
def call(conn, _opts) do
if (Enum.empty?(Plug.Conn.get_req_header(conn, "range"))) do
conn
else
file_path = "priv/static" <> conn.request_path
if File.exists? file_path do
stats = File.stat! file_path
filesize = stats.size
req = Regex.run(~r/bytes=([0-9]+)-([0-9]+)?/, conn |> Plug.Conn.get_req_header("range") |> List.first)
{req_start, _} = req |> Enum.at(1) |> Integer.parse
{req_end, _} = req |> Enum.at(2, filesize |> to_string) |> Integer.parse
file_end = ( filesize - 2) |> to_string
length = req_end - req_start + 1
conn
|> Plug.Conn.put_resp_header("Content-Type", "video/mp4")
|> Plug.Conn.put_resp_header("Accept-Ranges", "bytes")
|> Plug.Conn.put_resp_header("Content-Range", "bytes #{req_start}-#{req_end}/#{filesize}")
|> Plug.Conn.send_file(206, file_path, req_start, length)
|> Plug.Conn.halt
else
conn
end
end
end
end
如您所见,现在它只会发送 "video/mp4" content-Type,但您可以轻松地使某些东西适用于所有内容...
最后,为了让插件起作用,您需要将其放在项目端点文件中的 Plug.static 之前。
希望对某人有所帮助...
编辑:
对于那些感兴趣的人,我为此创建了一个 github/hex.pm 包:
Hex link
github link