mochiweb を使ったデータ分割読み込み処理

mochiweb_multipert:parse_multipart_request/2 を使う

データを分割して読み込んだりしたい時、
つまりストリームとしてファイルデータを扱いたいときは mochiweb_multipart モジュールの parse_multipart_request/2 を使います。

データが送られてきたタイミングで Callback が呼ばれ、処理されていきます。コールバック + 再帰を使った仕組みです。

Callback は Next という引数を一つ取るように書く必要があります。Next には以下の値が入ってきます。

  • {headers, Headers}
  • {body, Data}
  • body_end
  • eof

この辺をパターンマッチして、処理を分岐させて必要な処理をさせたら、Callback を返すだけです。
eof がきたら ok を返して処理を終了させています。

CSV ファイルをストリームでパースしていく(凄く適当版)

CSV パースはただ , で分割してるだけです。本来はもう少し頑張るべきかなと。
Erlang には csv モジュールがないので不便ですね ... 。

@itawasa に色々教えて貰ったので修正しました。curl -F で複数のデータを処理した時をイメージしてます。

...(Req) ->
  Callback = fun(Next) -> callback(Next, <<>>) end,
  case mochiweb_multipert:parse_multipart_request(Req, Callback) of
    {_, _, ok} ->
      %% 200 返す
      Req:ok(...);
    {_, _, {error, Reason}} ->
      %% 400 とか何か返す
      Req:respond(...)
  end.

callback({headers, _Headers}, Rest) ->
  %% 本来はここでヘッダーチェックする
  fun(Next) -> callback(Next, <<>>) end;
callback({body, Data}, Rest) ->
  case read_line(re:split(list_to_binary([Rest, Data]), <<"\\r\\n|\\n">>)) of
    {ok, NewRest} ->
      fun(Next) -> callback(Next, NewRest) end;
    {error, Reason} ->
      %% エラー動作は error_callback で処理をやめる
      fun(Next) -> error_callback(Next, Reason) end;
  end;
callback(body_end, Rest) ->
  fun(Next) -> callback(Next, Rest) end;
callback(eof, Rest) ->
  ?debugVal(Rest),
  ok.

error_callback(eof, Reason) ->
  {error, Reason};
error_callback(_, Reason) ->
  fun(Next) -> callback(Next, Reason) end;

%% ここでのサンプルは CSV が 3 つ区切り前提
-spec read_line([binary()]) -> {ok, binary()} | {error, {invalid_line, binary()}}.
read_line([Binary]) ->
  {ok, Binary};
read_line([<<>>|Tail]) ->
  read_line(Tail);
read_line([Binary|Tail]) ->
  case re:split(Binary, <<",">>, []) of
    [Row1, Row2, Row3] ->
      %% ここに Row1, Row2, Row3 を処理する関数
      %% たとえば ets:insert(?TABLE, {Row1, Row2, Row3}) など
      read_line(Tail);
    _Tokens ->
      {error, {invalid_line, Binary}}
  end.