【问题标题】:Serving HTTP range request with phoenix?用phoenix服务HTTP范围请求?
【发布时间】:2016-08-03 06:34:53
【问题描述】:

我目前正在使用 phoenix 开发一个网站,并且有一个应该在后台播放的视频部分。

虽然它在 Chrome 和 Firefox 上运行良好,但在 Safari 上却无法运行。

我怀疑这是因为牛仔没有正确处理 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

当它应该是 206 时,如 nginx 服务器所示:

$ 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

【问题讨论】:

  • 为什么要通过牛仔来提供静态内容?
  • @ardhitama :嗯,主要是因为它是使用 phoenix 的,而且我仍处于早期开发阶段……而且,据我所知,cowboy 提供的静态内容非常好。但是,如果我不能提供范围请求,我可能会通过 nginx 和反向代理到我的凤凰服务器,但我更愿意解决这个问题。

标签: elixir phoenix-framework cowboy


【解决方案1】:

Cowboy 似乎(还)不支持 Range 标头,因此您需要为此使用不同的 Web 服务器。

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

【讨论】:

  • 你是对的,Range 还不支持,但是我找到了一种使用 Plugs 的方法,请参阅下面的答案...
  • 就我个人而言,我宁愿有一个符合 RFC 的实现。但如果它对你有用,那就太好了!干杯;-)
  • 这仅用于开发目的......不幸的是,我现在没有时间创建一个符合 RFC 的实现......我可能会在一个月内使用符合 RFC 的插件创建一个 github ,但现在它对我有用;-)
  • 嘿,我并不是要挑战你执行 RFC 兼容的实现 ;-) 只是说,你不应该基于此在生产中运行高流量视频流服务 ;-P
【解决方案2】:

我找到了一种使用 Plugs 自己完成的方法... 因此,如果有人想使用 Phoenix / Elixir 服务 Range Request,这就是您必须做的(这是非常基本的,不考虑 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”内容类型,但您可以轻松地让某些东西适用于所有内容...

最后,要让 Plug 工作,您需要将它放在 Project Endpoint 文件中的 Plug.static 之前。

希望它可以帮助某人......

编辑: 对于那些感兴趣的人,我为此创建了一个 github/hex.pm 包: Hex link
github link

【讨论】:

    猜你喜欢
    • 2019-04-26
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    • 2015-02-04
    • 1970-01-01
    • 2022-06-28
    • 2012-01-07
    • 1970-01-01
    相关资源
    最近更新 更多