如何使用Plug.Conn读取phoenix控制器中的小块数据

时间:2017-06-14 10:42:53

标签: elixir phoenix-framework cowboy

我的目标是能够在phoenix控制器中处理分块的HTTP请求。我认为解决方案是使用Plug.Conn.read_body但是我收到错误或超时。

目前我认为最好的解决方案是自定义解析器。

defmodule Plug.EventStreamParser do
  @behaviour Plug.Parsers
  alias Plug.Conn

  def parse(conn, "text", "event-stream", _headers, _opts) do
    Conn.read_body(conn, length: 2, read_length: 1, read_timeout: 1_000)
    |> IO.inspect
    {:ok, %{}, conn}
  end
end

但是我总是在检查线上得到{:error :timeout}

1 个答案:

答案 0 :(得分:3)

Plug.Conn.read_body/2只读取请求正文的一个块。您需要递归调用它才能读取所有内容。你也不需要写一个解析器来只读大块的身体(我不认为如果我正确地理解你的问题,解析器甚至可以做到这一点);如果请求的Content-Type不是默认情况下Plug解析的请求,则可以从控制器中调用Plug.Conn.read_body/2

这是从控制器递归调用Plug.Conn.read_body/2的一个小实现:

defmodule MyApp.PageController do
  use MyApp.Web, :controller

  def index(conn, _params) do
    {:ok, conn} = read_body(conn, [length: 1024, read_length: 1024], fn chunk ->
      # We just print the size of each chunk we receive.
      IO.inspect byte_size(chunk)
    end)
    text conn, "ok"
  end

  def read_body(conn, opts, f) do
    case Plug.Conn.read_body(conn, opts) do
      {:ok, binary, conn} ->
        f.(binary)
        {:ok, conn}
      {:more, binary, conn} ->
        f.(binary)
        read_body(conn, opts, f)
      {:error, term} ->
        {:error, term}
    end
  end
end

以大约1024字节的块的形式读取正文(它不能保证返回的二进制文件与请求的大小完全相同)。使用以下请求POST 4000个字节:

$ head -c 4000 /dev/urandom | curl -XPOST http://localhost:4000 --data-binary @- -H 'Content-Type: application/vnd.me.raw'
ok

以下内容记录在控制台中:

[info] POST /
[debug] Processing by MyApp.PageController.index/2
  Parameters: %{}
  Pipelines: [:api]
1024
1024
1024
928
[info] Sent 200 in 3ms