Symfoware

Symfowareについての考察blog

elixir + cowboyでwebアプリ その6 画像のアップロードとpostの最大値変更

elixir + cowboyで、バイナリファイルをアップロードする機能を
実装してみます。


サンプル



mix new imageとして、新しくアプリケーションを作成しました。
ソースはこんな感じです。

・mix.exs


  1. defmodule Image.Mixfile do
  2. use Mix.Project
  3. def project do
  4.     [app: :image,
  5.      version: "0.0.1",
  6.      elixir: "~> 1.0",
  7.      build_embedded: Mix.env == :prod,
  8.      start_permanent: Mix.env == :prod,
  9.      deps: deps]
  10. end
  11. def application do
  12.     [
  13.         mod: {Image, []},
  14.         applications: [:logger, :cowboy]
  15.     ]
  16. end
  17. defp deps do
  18.     []
  19. end
  20. end




・lib/image.ex


  1. defmodule Image do
  2. def start(_type, _args) do
  3.     dispatch = :cowboy_router.compile([
  4.      { :_, [
  5.          {"/upload", Image.Handler, []},
  6.      ]}
  7.     ])
  8.     
  9.     {:ok, _} = :cowboy.start_http(:http, 100, [{:port, 8080}], [{:env, [{:dispatch, dispatch}]}])
  10.     ImageServer.Supervisor.start_link
  11. end
  12. end





・lib/imageServer.ex


  1. defmodule ImageServer.Supervisor do
  2. use Supervisor
  3. def start_link() do
  4.     {:ok, sup} = Supervisor.start_link(__MODULE__, :ok)
  5. end
  6. def init(:ok) do
  7.     {:ok, {{:one_for_one, 10, 10}, []}}
  8. end
  9. end





・lib/imageHandler.ex


  1. defmodule Image.Handler do
  2. def init(_type, req, []) do
  3.     {:ok, req, :no_state}
  4. end
  5. def handle(request, state) do
  6.     
  7.     # requestからPOSTデータを取得
  8.     {:ok, image, _} = :cowboy_req.body(request)
  9.     {:ok, file} = File.open("image.jpg", [:write])
  10.     IO.binwrite(file, image)
  11.     File.close(file)
  12.     
  13.     { :ok, reply } = :cowboy_req.reply(
  14.      200,
  15.      [ {"content-type", "text/html"} ],
  16.      "ok\n",
  17.      request
  18.     )
  19.     {:ok, reply, state}
  20. end
  21. def terminate(reason, request, state) do
  22.     :ok
  23. end
  24. end





テスト用のプログラムはPythonで作成しました。

・sample.py


  1. # -*- coding:utf-8 -*-
  2. import urllib
  3. import urllib2
  4. import json
  5. def main():
  6.     url = 'http://192.168.1.102:8080/upload'
  7.     with open('cc2.jpg', 'rb') as f:
  8.         postval = f.read()
  9.     
  10.     request = urllib2.Request(url, data=postval)
  11.     response = urllib2.urlopen(request)
  12.     ret = response.read()
  13.     
  14.     print ret
  15. if __name__ == '__main__':
  16.     main()





サーバーを起動。


# iex -S mix




Pythonのスクリプトを実行すると、画像がサーバー側に保存されるはずです。
意外とお手軽でした。


$ python sample.py
ok







8MBを超えるファイル



容量の大きいファイルをアップロードしようとすると、こんなエラーになります。


13:27:00.418 [error] Process #PID<0.231.0> raised an exception
** (ErlangError) erlang error: [reason: {:badmatch, ...
    (cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4





調べてみると、POSTの上限は8MBの模様。
こちらを参考に、上限を変更します。
http://ninenines.eu/docs/en/cowboy/HEAD/guide/req_body/

・lib/imageHandler.ex


  1. defmodule Image.Handler do
  2. def init(_type, req, []) do
  3.     {:ok, req, :no_state}
  4. end
  5. def handle(request, state) do
  6.     
  7.     # requestからPOSTデータを取得
  8.     # lengthオプションで、受け取るバイト数の上限を指定
  9.     {:ok, image, _} = :cowboy_req.body(request, [{:length, 20000000}] )
  10.     {:ok, file} = File.open("image.jpg", [:write])
  11.     IO.binwrite(file, image)
  12.     File.close(file)
  13.     
  14.     { :ok, reply } = :cowboy_req.reply(
  15.      200,
  16.      [ {"content-type", "text/html"} ],
  17.      "ok\n",
  18.      request
  19.     )
  20.     {:ok, reply, state}
  21. end
  22. def terminate(reason, request, state) do
  23.     :ok
  24. end
  25. end




これで、大きな容量のファイルもアップロードできるようになりました。






リクエストヘッダー



画像のファイル名をリクエストヘッダーに含めてみます。
こちらを参考にしました。
http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req/


・lib/imageHandler.ex


  1. defmodule Image.Handler do
  2. def init(_type, req, []) do
  3.     {:ok, req, :no_state}
  4. end
  5. def handle(request, state) do
  6.     
  7.     # ヘッダーからファイル名を取得
  8.     {file_name, _} = :cowboy_req.header("file_name", request)
  9.     
  10.     # requestからPOSTデータを取得
  11.     # lengthオプションで、受け取るバイト数の上限を指定
  12.     {:ok, image, _} = :cowboy_req.body(request, [{:length, 20000000}] )
  13.     {:ok, file} = File.open(file_name, [:write])
  14.     IO.binwrite(file, image)
  15.     File.close(file)
  16.     
  17.     { :ok, reply } = :cowboy_req.reply(
  18.      200,
  19.      [ {"content-type", "text/html"} ],
  20.      "ok\n",
  21.      request
  22.     )
  23.     {:ok, reply, state}
  24. end
  25. def terminate(reason, request, state) do
  26.     :ok
  27. end
  28. end





送信側のサンプル

・sample.py


  1. # -*- coding:utf-8 -*-
  2. import urllib
  3. import urllib2
  4. import json
  5. def main():
  6.     url = 'http://192.168.1.102:8080/upload'
  7.     with open('cc2.jpg', 'rb') as f:
  8.         postval = f.read()
  9.     
  10.     request = urllib2.Request(url, data=postval)
  11.     # ヘッダーにファイル名を付与
  12.     request.add_header('file_name', 'cc2.jpg')
  13.     
  14.     response = urllib2.urlopen(request)
  15.     ret = response.read()
  16.     
  17.     print ret
  18. if __name__ == '__main__':
  19.     main()




これで、ちゃんとヘッダーに仕込んだファイル名で保存してくれました。
最終的なサンプルをこちらに置いておきます。

elixir + cowboy 画像のアップロードとpostの最大値変更

関連記事

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2015/08/02(日) 17:32:38|
  2. Erlang
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
<<elixirからepgsqlを使用して、PostgreSQLに接続する | ホーム | elixir + cowboyでwebアプリ その5 デプロイ>>

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://symfoware.blog68.fc2.com/tb.php/1775-069d9c12
この記事にトラックバックする(FC2ブログユーザー)