まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:lua

このブログエントリの続きです:
LUA で FlashAir の特定フォルダ内の最新ファイルを探す

↑この時の内容は、特定フォルダ「内」の最新ファイルを探す LUA スクリプトの紹介でしたが、ちょっとパワーアップして特定フォルダ「以下」の最新ファイルを探すようにしています。

特定フォルダ以下、といっても無限に探してくれるものではありません。実際のデジカメの挙動では、写真を撮影すると、"/DCIM/(どこかのディレクトリ)/***.jpg" みたいな感じで画像ファイルが格納されるらしいです。この「(どこかのディレクトリ)」という部分がメーカーによって変わったり、ある一定以上のファイルが1つのフォルダに貯まると別のディレクトリを作ったりして格納されるらしいのです(また聞き)。なので、この「(どこかのディレクトリ)」部分は最初に固定して考えるわけにはいかないが、これ以上の孫階層が作られるといったものでもない、ということだと思います。

で、その前提で「/DCIM 以下にある最新ファイルを探す」というスクリプトに作り変えてみました。要はサブフォルダが作られるとしたら1階層だけ作られて上で格納されるファイルの中で最新のものを探す、という内容に改良してみました、というものです:
last_fname = ""
last_fpath = ""
max_mod = 0
fpath = "/DCIM"  目的のフォルダ
for dirname in lfs.dir(fpath) do
  dirpath = fpath .. "/" .. dirname
  mod_dir = lfs.attributes( dirpath, "mode" )
  if mod_dir == "directory" then サブフォルダを再度調べる
    for filename in lfs.dir(dirpath) do
      filepath = dirpath .. "/" .. filename
      mod = lfs.attributes( filepath, "modification" )
      if mod > max_mod then
      max_mod = mod
        last_fname = filename
        last_fpath = filepath
      end
    end
  else fpath 直下にファイルがあった場合は対象にする
    mod = lfs.attributes( dirpath, "modification" )
    if mod > max_mod then
      max_mod = mod
      last_fname = dirname
      last_fpath = dirpath
    end
  end
end

   :
 (この時点で last_filepath に最新ファイルのパスが格納されている)
   :

これでかなり実用的になったはず。。


以前に東芝 FlashAir 内の特定ファイルをウェブサーバーにアップロードする、という lua スクリプトを作って紹介したことがありました:

一応、このスクリプトは動くのですが、残念なことに固定ファイルしかアップロードできませんでした。理想を言えば「FlashAir に最後に作成されたファイル」を調べることができれば、LUA_SD_EVENT イベントをフックして、「新しいファイルが追加されたら、そのファイルをアップロードする」ということができるようになるのですが、このブログエントリを記述した頃はまだそこまで lua を分かっていませんでした。

で、今乗ってる電車の目的地までしばらく時間がある時間と気持ちに余裕ができたので、FlashAir の特定ディレクトリ内で最も最近作成されたファイルを調べるスクリプトを作ってみました(赤字はコメントです):
last_filepath = ""
max_mod = 0
fpath = "/DCIM/100__TSB" 目的のフォルダ
for filename in lfs.dir(fpath) do 目的フォルダ内の全ファイルを探す
  filepath = fpath .. "/" .. filename
  mod = lfs.attributes( filepath, "modification" ) ファイルの最終更新日時
  if mod > max_mod then 更新日時が最新(最大)のファイルを探す
    max_mod = mod
    last_filepath = filepath
  end
end

   :
(この時点で last_filepath に最新ファイルのパスが格納されている) :

このスクリプトは FlashAir 内の \DCIM\100__TSB というフォルダの中を調べて最も作成日時(実際には更新日時)が新しいファイルを探して last_filepath という変数にそのファイルのファイルパスを代入する、という処理を実行しています。

というわけで、このスクリプトを使って上述リンク先の前回作成した lua スクリプトを改良して、「FlashAir の \DCIM\100__TSB フォルダ内に新しいファイルが作られたらウェブサーバーにアップロードする」という lua スクリプトを作ってみました:
last_fname = ""
last_fpath = ""
max_mod = 0
fpath = "/DCIM/100__TSB"
for filename in lfs.dir(fpath) do
  filepath = fpath .. "/" .. filename
  mod = lfs.attributes( filepath, "modification" )
  if mod > max_mod then
    max_mod = mod
    last_fname = filename
    last_fpath = filepath
  end
end

boundary = "1234567890"
contenttype = "multipart/form-data; boundary=" .. boundary
mes = "--" ..  boundary .. "\r\n"
  .."Content-Disposition: form-data; name=\"file\"; filename=\""..last_fname.."\"\r\n"
  .."Content-Type: image/png\r\n\r\n"
  .."\r\n"
  .."--" .. boundary .. "--\r\n"

blen = lfs.attributes(last_fpath,"size") + string.len(mes) - 17
b, c, h = fa.request{url = "http://XX.XX.XX.XX/up.php",
  method = "POST",
  headers = {["Content-Length"] = tostring(blen),
  ["Content-Type"] = contenttype},
  file = fpath,
  body = mes
}


これを FlashAir の \lua\upload.lua という名前で作成し、\SD_WLAN\config ファイルに以下の1行を追加すると、FlashAir の \DCIM\100__TSB フォルダに新しいファイルができる度にそのファイルはウェブサーバーにアップロードされる、という処理が実現できるようになります:
LUA_SD_EVENT=/lua/upload.lua


そしてウェブサーバー(XX.XX.XX.XX)側のドキュメントルートに up.php というアップロードの受け口となるモジュールが用意されていればこれでアップロードできます。こちらのサンプルは上述の前回リンクの中で紹介しているサンプルのようなものになります(サンプルは PHP です)。


Lua のファイルシステム API についてはこちらのリファレンスを参照してください:
http://keplerproject.github.io/luafilesystem/manual.html#reference
 

東芝の(いい意味で)変態な SD カード "FlashAir" 。実は以前のブログエントリでは以前のバージョンの FlashAir のこんなハックを紹介したことがありました:


比較的最近のモデルでは lua 言語によるスクリプト処理が可能になったり、「ファイルが FlashAir カードに追加された」などのイベントをハンドリングして処理を実行したり、といったことも可能になり、ますます変態チックな度合いが上がっているようです:



で、せっかく lua スクリプトが使えるようになったのであれば、このカードの中のファイルを(例えば FlashAir カードにファイルが追加されたタイミングなどで)サーバーにアップロードして、サーバー側ではアップロードされた画像ファイルになんらかの(例えば「保存する」などの)処理を実行する、という一連のスクリプトも記述できるのではないか、と考えました。

サーバー側の処理はアップロードされたファイルを取り出して・・・という内容になると思うので、一般的なファイルアップロード処理を行うことになります。

一方、クライアント(FlashAir カード)側の処理は、やることは簡単なのですが、何しろ普段名前を聞くくらいでしか知らなかった lua という軽量言語を触ったことがありませんでした。加えて、ちょっと特殊な言語のようで結局、未だにデバッグ方法を理解していません(苦笑)。 FlashAir も変態でしたが、lua は lua でまたどM向けプログラミング言語というか・・・ 「変態」とか「どM」とか、IT とは異なる世界の話をしている気分になってきます。 (^^;

まあ、でもなんとかファイルアップロードができることを確認したスクリプトが作れました。もしかしたら私以外の変態な皆様の役に立つかもしれないと思って公開することにします(赤字はコメントなので実際には不要、というか残したままだと正しく動きません):
boundary = "1234567890" 適当な文字列
contenttype = "multipart/form-data; boundary=" .. boundary
fname = "sample.png" 転送するファイル名
fpath = "/DCIM/100__TSB/" .. fname 転送するファイルのフルパス
mes = "--" ..  boundary .. "\r\n"
  .."Content-Disposition: form-data; name=\"file\"; filename=\""..fname.."\"\r\n"
  .."Content-Type: image/png\r\n\r\n"
  .."\r\n"
  .."--" .. boundary .. "--\r\n"

blen = lfs.attributes(fpath,"size") + string.len(mes) - 17
b, c, h = fa.request{url = "http://xx.xx.xx.xx/up.php", http://xx.xx.xx.xx/up.php というアップローダーに転送
  method = "POST",
  headers = {["Content-Length"] = tostring(blen),
  ["Content-Type"] = contenttype},
  file = fpath,
  body = mes
}

#だいたい lua のコメントってどう書くのかと??

この例では FlashAir 内の /DCIM/100__TSB/sample.png というファイルを http://xx.xx.xx.xx/up.php というアップローダーに転送することを想定した、マルチパートにすらしていないシンプルなスクリプトです。固定の同じファイルだけを送信するような内容になっていますが、これは実際にはファイル名のパラメータ渡しなどで対応できないかな、と思ってます。

受け取る側の up.php は例えばこんな感じで:
<?php
$name = $_FILES["file"]["name"]; // ファイル名
$mimetype = $_FILES["file"]["type"]; // Content-Type
$filesize = $_FILES["file"]["size"]; // ファイルサイズ
$tmpname = $_FILES["file"]["tmp_name"]; // 一時ファイル名(ここに実体がある)

$filename = "/var/www/html/imgs/" . $name; $result = @move_uploaded_file( $tmpname, $filename ); // 一時ファイルを Document Root 以下に移動するだけ echo( $result ); ?>

こちらはある意味で「一般的な」 PHP アップローダーの中身です。 $_FILES 変数に入った情報を元にファイル名やサイズ、そしてファイルの実体を取り出しています。この例では単純にアップロードされたファイルをドキュメントルート以下に移動するだけの内容です。これが http://xx.xx.xx.xx/up.php という URL でアクセスできる PHP サーバー上に用意されている、という想定です。
2015062401


この PHP では単純にアップロードされたファイルをドキュメントルート以下に置き直しているだけですが、その画像を使って PHP で更に別の処理を行う、なんてことももちろんできます。また PHP である必要もなく、ごく一般的な HTTP ファイルアップローダーの仕組みが用意されていればよい、という認識で大丈夫です。


これら2つのファイルを FlashAir および PHP サーバー側にそれぞれ用意しておくことで FlashAir からのファイルアップロードが実現できました。後はこれを HTTP 経由で呼び出すか、あるいは FlashAir の SD カードにファイルが追加されたタイミングで実行する、などの方法で lua を動かして使うことになります。



参考にしたのはこの辺りです:
- FlashAir Developers : チュートリアル
- FlashAir Developers : API ガイド
おまけ程度のツール置き場 - FlashAirをいじってわかったこと。


#いやあ、しかし lua は難しいというか、、、資料が少ないのも原因なんだろうけど・・・
 

このページのトップヘ