【问题标题】:Reading data as a stream using Elixir and Hackney使用 Elixir 和 Hackney 将数据作为流读取
【发布时间】:2015-12-09 04:08:10
【问题描述】:

我正在使用hackney 并尝试使用here 中的说明将请求的主体作为流读取。 由于那里的代码是用 Erlang 编写的,我不得不对其进行调整以在 Elixir 中工作:

  def read_body(max_length, client, acc) when max_length > byte_size(acc) do
    case :hackney.stream_body(client) do
      {:ok, data} -> read_body(max_length, client, acc <> data)
      :done -> {:ok, acc}
      {:error, reason} -> {:error, reason}
    end
  end

  def read_body(max_length, client, acc) do
    acc
  end

我使用以下方式运行它:

acc = HackneyTutorial.read_body(20, client, "")                     
# I have my own methods for retrieving the client

问题是我希望使用上面的代码只从正文中读取前 20 个字节的数据,但是 acc 变量获取整个正文,它要么不考虑守卫 when max_length &gt; byte_size(acc) ,或者它只是在第一次调用 :hackney.stream_body/1 时读取所有正文。

你认为什么可以解决这个问题?

【问题讨论】:

    标签: elixir


    【解决方案1】:

    您的read_body/3 函数正在发生两件事之一:

    1. 正如您所提到的,整个正文从 :hackney.stream_body/1 调用中立即返回,该调用恰好大于 max_length,然后导致 read_body/3 的第二个定义被调用。
    2. 正文的部分被返回,因为read_body/3 递归执行直到acc 的大小不再小于max_length,然后导致read_body/3 的第二个定义被调用(结果与1 相同)。

    :hackney.stream_body/1 函数和 read_body/3 守卫都不会实际上限制基于 max_length 的返回正文的大小。在acc 大于指定的max_length 的两种情况下,:done 大小写(或{:error, reason} 大小写)将永远不会匹配。

    为了限制返回的正文的大小,一种解决方案是在 case 表达式中添加两个额外的保护,并删除不带保护的 read_body/3 的二级定义:

    def read_body(max_length, ref, acc) do
      case :hackney.stream_body(ref) do
        {:ok, _} when byte_size(acc) > max_length ->
          read_body(max_length, ref, acc)
        {:ok, data} when (byte_size(data) + byte_size(acc)) > max_length ->
          read_body(max_length, ref, acc <> String.slice(data, 0, max_length - byte_size(acc)))
        {:ok, data} ->
          read_body(max_length, ref, acc <> data)
        :done ->
          {:ok, acc}
        {:error, reason} ->
          {:error, reason}
      end
    end
    

    另一种解决方案可能是从max_length 中减去返回数据的大小,直到达到0,然后忽略其余数据:

    def read_body(0, ref, acc) do
      case :hackney.stream_body(ref) do
        {:ok, _} ->
          read_body(0, ref, acc)
        :done ->
          {:ok, acc}
        {:error, reason} ->
          {:error, reason}
      end
    end
    def read_body(max_length, ref, acc) do
      case :hackney.stream_body(ref) do
        {:ok, << data :: binary-size(max_length), _ :: binary >>} ->
          read_body(0, ref, acc <> data)
        {:ok, data} ->
          read_body(max_length - byte_size(data), ref, acc <> data)
        :done ->
          {:ok, acc}
        {:error, reason} ->
          {:error, reason}
      end
    end
    

    两种解决方案的结果相同,但显示了解决同一问题的几种不同方法。

    【讨论】:

      猜你喜欢
      • 2017-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-21
      • 2020-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多