2018年6月25日月曜日

アップロードされたファイルを Node-RED で受け取る方法

例えば画像を Node-RED へアップロードしたい、とかの時です。
下のフロー図は、ファイルを送信できるアップロードページと、送られてきたファイルを鯖上に保存するフローのサンプルです。
フローの JSON コード貼ると化けちゃうブログなので、下記の説明を読んで各自で自作してくださいw

ただし環境によっては file ノード(ファイル作成ノード)がない場合もあります。(IBM Cloud のボイラーテンプレートで用意された Node-RED とか)
その場合は file ノードの代わりに、データベースなどへの保存の処理を行う必要があるわけですが、それはまた別のお話……

では、個々のノードについてザッと説明します。

アップロードページのフロー

まずは動作確認のためにも、ファイルをアップロードするためのページを用意します。

「[get] /upload」http 入力ノード

Node-RED 鯖の /upload に GET でアクセス(単にブラウザでアクセス)されたときのフローがここからスタート。

「HTML」template ノード

ここでファイルをアップロードできる HTML を msg.payload にぶち込みます。
HTML はこんな感じ。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>アップロード</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input name="file" type="file" id="file"><br>
<input type="submit" value="アップロード">
</form>
</body>
</html>
ちゃんと form 要素の method属性は post に、enctype 属性は multipart/form-data に設定しましょう。

http 出力ノード

上記の HTML をここで返します。
それにより無事、アクセスしてきたブラウザにはアップロードページが表示されます。
特に何も設定しなくてもデフォで Content-Type: text/html として返してくれるので楽ちん。

ファイルを受け取ったときのフロー

こっからが本題。
先のページからファイルがアップされた時のフローです。

「[post] /upload」http 入力ノード

アップロードページと同じ URL だけど、POST だった場合はこっちがスタートします。
ファイルがアップされるはずなので、ノード編集画面の「ファイルのアップロード」にチェックを入れましょう。
チェック入れておかないと msg.payload にバイナリバッファが入っちゃって意味不明になります。
入れておくことでファイルは msg.req.files に格納され、msg.payload には HTML の form の各値が格納された状態になります。

「処理」function ノード

ここでは簡単なエラーチェックをしつつ、応答のフローとファイル保存のフローに分けています。
出力数は 2 にして、return の [0] は応答のフローへ、return の [1] はファイル保存のフローへ分かれるようにしました。
コードは以下の通り。
let files = msg.req.files;
if (files.length === 0) {
msg.payload = "ファイルが添付されてないよ";
return [msg, null];
}
msg.payload = "たぶん保存したよ";
//
let msg2 = {};
msg2.payload = files[0].buffer;
msg2.filename = "/home/user/" + files[0].originalname;
return [msg, msg2];
まず、msg.req.files の数を見て、ちゃんとファイルが含まれているかをチェックしています。
もしファイルが含まれていなかったら、ファイル保存のフローへは null を返すことで、フローが進まないようにしています。
他にも必要であれば msg.req.files[0].mimetype などを確認して、想定外のファイル形式を弾いたりしても良いでしょう。

で、問題がなかった場合、新たにファイル保存フロー用のオブジェクト msg2 をこしらえます。
そして次の「ファイル作成」ノードのために、msg2.payload にファイルのバイナリバッファを入れます。
あと、保存先とファイル名もここで設定してしまいます。
サンプルでは鯖上の /home/user/ へ元のファイル名のまま保存するようにしました。

「HTML」template ノード その2

応答フローの template ノードです。
ここでは応答用の HTML を作成して msg.payload に入れています。
予め先の「処理」function ノードで msg.payload にはメッセージが入れられているので、Mustache タグを利用して payload の内容を HTML に差し込んでいます。
↓こんな感じ。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>結果</title>
</head>
<body>
<p>{{payload}}</p>
</body>
</html>
ヒゲ便利w

http 出力ノード

応答フローでも先の http 出力ノードを再利用。えこえこ。

「ファイル作成」file ノード

鯖上にファイルを保存します。
ノード編集画面で「動作」を「ファイルを上書き」にして「メッセージの入力のたびに改行を追加」のチェックは外しておきましょう。
「ファイル名」は先の「処理」function ノードで設定済みなので、ここでは空にしておきます。
(以前は何か入力されていても、msg 側が優先されてたけど、最近 msg 側で設定済みなのにノード編集画面でも何か入力されてると警告されるようになったっぽぃ)

以上、個々のノードの説明でした。
実際に /upload へブラウザでアクセスしてみて、動作を確認してみましょう!
そして何か間違えている場合、またはもっといい方法がある場合は教えてくださいw

おしまーいヽ(=´▽`=)ノ


2018年6月21日木曜日

Node-RED で base64 エンコードとかデコードしたい時

Node-RED は受信した画像とかローカルのファイルとかをバイナリバッファでやり取りするけど、Google さんの Cloud Vision API に画像送りたい時とかには base64 エンコードして送らなきゃいけなかったりするときのなんやかんや。

方法1:node-red-node-base64ノードを使う。

超楽ちん。
https://flows.nodered.org/node/node-red-node-base64
こいつをメニュー「パレットの管理」から追加して使うだけ。
msg.payload がバイナリバッファなら base64 にしてくれて、逆に base64 だったらバイナリバッファにしてくれます。

方法2:Node.js の Buffer クラスを利用する。

下記、new Buffer() は非推奨になりました。
今後は変わりに Buffer.from() を使いましょう。

https://nodejs.org/api/buffer.html

function ノードで
let buffer = new Buffer(msg.payload);
msg.payload = buffer.toString('base64');
return msg;
とかすれば base64 に変換できます。
これも楽ちん。
逆にバイナリバッファにデコードしたいときは
msg.payload = new Buffer(msg.payload, 'base64');
return msg;
てな感じ。

おしまい。