14
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ElixirAdvent Calendar 2022

Day 5

Elixir Phoenix の REST API でバイナリデータを受信する

Last updated at Posted at 2022-12-16

はじめに

LiveView が盛り上がっている中 REST API かよ、という感じですが、画像 AI を提供するエンドポイントを REST API として提供する形態を想定しています

参考にした Elixir Forum の質問

JSON データで受信する

特に何も考えずに Phoenix で REST API を構築すると、 Controller でリクエストを受ける実装は以下のようになります

defmodule ApiWeb.PredictionController do
  use ApiWeb, :controller

  action_fallback ApiWeb.FallbackController

  # param に Map でリクエストが入ってくる
  def index(conn, param) do
    ...
  end

...
end

例えば curl で以下のように送信したとします

curl -XPOST http://localhost:4000/api \
  --data "{\"xxx\":\"aaa\"}" \
  --header "Content-Type:application/json"

すると JSON がデコードされ、 param には以下のような Map が入ってきます

%{"xxx" => "aaa"}

ここに JSON ではなくバイナリデータを送ってみます

curl -XPOST http://localhost:4000/api \
  --data-binary @sample.jpg \
  --header "Content-Type:image/jpeg"

そうするとデコードできないので、空の Map が入ってくるようになります

%{}

JSON 形式で画像データを送信したい場合、画像を文字列に変換する必要があります

というわけで、よく BASE64 が使用されます

curl で BASE64 エンコードした画像を以下のように送信します

echo {\"image\":\"$(base64 -i sample.jpg)\"} | \
  curl -XPOST \
    --data @- \
    --header "Content-Type:application/json"

ちょっと解説

  • base64 -i <画像ファイルパス> で画像を base64 文字列に変換できます

  • $(<シェルコマンド>) でシェルコマンドの実行結果を値としてシェルコマンドの中に展開します

  • echo {\"image\":\"$(base64 -i sample.jpg)\"} で {"image":"<画像のBASE64変換結果>"} を標準出力に渡します(\ はエスケープ文字で、 " を出力するために使っています)

  • | (パイプ)で標準出力を次のシェルコマンドに渡します (Elixir でいうところの |>

  • @-| で渡ってきた値を受けます(つまり、 {"image":"<画像のBASE64変換結果>"} が JSON として送信される)

この場合、  Controller では以下の様にパターンマッチして受け取り、 Base.base64 でデコードすることができます

...
  def index(conn, %{"image" => base64}) do
    binary = Base.decode64!(base64)
    ...
  end
...

この実装で確かに画像データを REST API で受けて処理することは可能です

しかし、いちいち BASE64 エンコードとデコードを挟むのは無駄ですよね

というわけで本題です

バイナリデータのまま受信します

バイナリデータで受信する

答えは以下の様な実装になります

curl 送信

curl -XPOST http://localhost:4000/api \
  --data-binary @sample.jpg \
  --header "Content-Type:image/jpeg"

Phoenix 受信

...
  def index(conn, %{}) do
    {:ok, binary, _} = Plug.Conn.read_body(conn)
    ...
  end
...

Plug.Conn.read_body で接続 conn を読み込むと、そのままバイナリデータを取得することができます

まとめ

すっごく単純な答えでしたが、普通は JSON しか送受信しないのでなかなか辿り着きませんでした

14
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?