nginx 1.6.2 + lua-nginx-moduleで簡易ファイルアップローダー

nginxの組み込みLuaが動く環境を作ってみました。
Debian 7 + nginx 1.6.2 + lua-nginx-moduleの環境構築

具体的な使い方を調べてみます。
最終的には、postされたファイルを保存するLuaプログラムを作ってみます。


post値の受け取り



http://wiki.nginx.org/HttpLuaModule

こちらを参考にLuaでpost値を受け取る方法を調べてみました。

nginx.confはこんな感じ。


worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include     mime.types;
    default_type application/octet-stream;

    sendfile        on;

    keepalive_timeout 65;

    server {
        listen     80;
        server_name localhost;

        location / {
            content_by_lua_file /opt/nginx/lua/sample.lua;
        }
    }
}





/opt/nginx/lua/sample.luaはこんなかんじです。
以降、このファイルを編集して動作を試してみます。


  1. -- リクエストの内容を解析
  2. ngx.req.read_body()
  3. -- post値を取得
  4. local args, err = ngx.req.get_post_args()
  5. if not args then
  6.     ngx.say("failed to get post args: ", err)
  7.     return
  8. end
  9. -- key:valueの形式で応答
  10. for key, val in pairs(args) do
  11.     ngx.say(key, ": ", val)
  12. end




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


  1. # -*- coding:utf-8 -*-
  2. import urllib
  3. import urllib2
  4. url = "http://192.168.1.102/"
  5. params = urllib.urlencode({'name':1, 'item_id':2})
  6. req = urllib2.Request(url, params)
  7. res = urllib2.urlopen(req)
  8. print res.read()





動かしてみると、こんな応答があります。
Luaプログラムが動いてくれているようです。


$ python post.py
item_id: 2
name: 1




最初、画像データをbase64エンコードしてpostすればいいと
思っていたのですが、ちょっと大きめの画像だと
あっさり500エラーになり頓挫しました。

別の作戦を考えてみます。





画像データのpost



nginx-lua and nginx upload module
こちらのサンプルが参考になりそうです。

かなりハマったのですが、やっていて理解したことは

・そんなに大きくないデータ(75KB未満?)だと、ngx.req.get_body_data()で
postされたデータが取得できる。

・それなりの大きさだと、nginxが自動的にテンポラリファイルに保存する。
テンポラリファイルの名前はngx.req.get_body_file()で取得できる

上記のことを踏まえて、こんなluaプログラムを書いてみます。


  1. -- bodyの解析
  2. ngx.req.read_body()
  3. -- body_dataを取得してみる
  4. local req_body = ngx.req.get_body_data()
  5. -- もし取得できていなかったら、データはファイルに行っている
  6. if not req_body then
  7.     -- テンポラリのファイル名を取得。内容を全部読み込む
  8.     local req_body_file_name = ngx.req.get_body_file()
  9.     local file = io.open(req_body_file_name, 'rb')
  10.     req_body = file:read('*a')
  11.     file:close()
  12. end
  13. -- ファイル保存
  14. f = io.open("/tmp/test.png", "wb")
  15. f:write(req_body)
  16. f:close()
  17. ngx.say('ok')




データを送ってみるPythonプログラムはこちら。


  1. # -*- coding:utf-8 -*-
  2. import urllib
  3. import urllib2
  4. import base64
  5. url = "http://192.168.1.102/"
  6. with open('sample.png', 'rb') as f:
  7.     image = f.read()
  8. req = urllib2.Request(url, data=image)
  9. res = urllib2.urlopen(req)
  10. print res.read()




出来ました。

518_01.png




HTTP Error 413: Request Entity Too Large



はしゃいで15MBぐらいの画像アップロードを試してみると、


urllib2.HTTPError: HTTP Error 413: Request Entity Too Large



というエラーが発生します。

Nginx での 413 Request Entity Too Large エラーの対処法
こちらを参考にnginx.confを編集。

client_max_body_size 20M;の記載を追加します。


worker_processes 1;

events {
    worker_connections 1024;
}

http {
    include     mime.types;
    default_type application/octet-stream;

    sendfile        on;

    keepalive_timeout 65;
    client_max_body_size 20M;

    server {
        listen     80;
        server_name localhost;

        location / {
            content_by_lua_file /opt/nginx/lua/sample.lua;
        }
    }
}




これでアップロードできるようになりました。





ファイルの保存先



バイナリでデータが送信できるようになったので、msgpackを使って
保存先のファイルパスと実データを送信ということもできそうです。

今回は、リクエストヘッダー情報にファイル名を含めることにしました。
リクエストヘッダーの情報は
ngx.req.get_headers()
で取得できます。


luaのプログラム


  1. -- bodyの解析
  2. ngx.req.read_body()
  3. -- body_dataを取得してみる
  4. local req_body = ngx.req.get_body_data()
  5. -- もし取得できていなかったら、データはファイルに行っている
  6. if not req_body then
  7.     -- テンポラリのファイル名を取得。内容を全部読み込む
  8.     local req_body_file_name = ngx.req.get_body_file()
  9.     local file = io.open(req_body_file_name, 'rb')
  10.     req_body = file:read('*a')
  11.     file:close()
  12. end
  13. -- ヘッダーから保存するときのファイル名を取得
  14. filename = ngx.req.get_headers()["filename"]
  15. -- ファイル保存
  16. f = io.open("/tmp/" .. filename, "wb")
  17. f:write(req_body)
  18. f:close()
  19. ngx.say(filename)
  20. ngx.say('ok')





テスト用のPythonプログラム


  1. # -*- coding:utf-8 -*-
  2. import urllib
  3. import urllib2
  4. import base64
  5. url = "http://192.168.1.102/"
  6. with open('sample.png', 'rb') as f:
  7.     image = f.read()
  8. req = urllib2.Request(url, data=image)
  9. req.add_header('filename', 'upload.png')
  10. res = urllib2.urlopen(req)
  11. print res.read()




これで狙い通りの動きになりました。



【参考URL】

http://wiki.nginx.org/HttpLuaModule

nginx-lua and nginx upload module

Nginx での 413 Request Entity Too Large エラーの対処法

Luaのお勉強 文字列の連結やtableの使い方

PythonからREST API経由でRedmineにチケットを登録する(XML,JSON使用)
関連記事

コメント

プロフィール

Author:symfo
blog形式だと探しにくいので、まとめサイト作成中です。
https://symfo.web.fc2.com/

PR

検索フォーム

月別アーカイブ