使用 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,因此您需要为此使用不同的网络服务器。

来源:https://github.com/ninenines/cowboy/issues/306

我找到了一种使用 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