1. はじめに
Node-Redが中継役として、http postされた画像を別サーバにhttp postする方法についてまとめました。
この方法を対向系を作成しながら模索した記事は別にしています。
2. 全体の概要
想定しているのは、カメラノードからhttp post でアップロードされた画像をラズパイが一旦引き取り、クラウド側へhttp postでアップロードするという構成です。次の図のような構成です。
カメラノードとしてM5Cameraを想定しています。M5Cameraがhttp postする方法は、こちらに挙げています。 M5CameraはIoTでいうところのエッジですので、いきなりクラウド側に上げる役割を持たせるのは、能力的にも、開発工数的にも、不安があります。その点、ラズパイは、パワーがあり、node-redで開発も容易ですので、フォグコンピューティングとしての役割を持たせるのがとても都合が良いです。
クラウド側も最近は、Node-Redが動くサーバがほとんどです。そうなると、今回の内容はそのままクラウド側にも流用できそうですが、今回は、旧来のCGIを対象として考えてみます。
3. フローの構築
3.1. 全体
全体のフローは次の通りです。
このフローのjsonは次です。(三角マークをクリックして展開して利用してください)
RelayImagehttpPost.json
[
{
"id": "46a5b4e9.1028e4",
"type": "tab",
"label": "フロー 2",
"disabled": false,
"info": ""
},
{
"id": "e4365738.d8df28",
"type": "http in",
"z": "46a5b4e9.1028e4",
"name": "",
"url": "/mypost01",
"method": "post",
"upload": true,
"swaggerDoc": "",
"x": 120,
"y": 120,
"wires": [
[
"39c6a8ed.47b6",
"99b3be79.f601c8"
]
]
},
{
"id": "7e14122.70cf86c",
"type": "http response",
"z": "46a5b4e9.1028e4",
"name": "",
"statusCode": "",
"headers": {},
"x": 460,
"y": 60,
"wires": []
},
{
"id": "39c6a8ed.47b6",
"type": "template",
"z": "46a5b4e9.1028e4",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "received",
"output": "str",
"x": 310,
"y": 60,
"wires": [
[
"7e14122.70cf86c"
]
]
},
{
"id": "a06d87ec.8581c8",
"type": "function",
"z": "46a5b4e9.1028e4",
"name": "wrap",
"func": "//let myb64 = msg.payload.toString('base64');\n\nlet mymsg2={};\n\nmymsg2.headers = {\n \"Content-Type\": \"multipart/form-data; boundary=------------------------1234567890987654\"\n}\n\nmymsg2.payload = '--------------------------1234567890987654\\r\\n'+\n'Content-Disposition: form-data; name=\"imagefile\"; filename=\"10x10.jpg\"\\r\\n'+\n'Content-Type: image/jpeg\\r\\n'+\n'Content-Transfer-Encoding: base64\\r\\n'+\n'\\r\\n'+\nmsg.payload +\n'\\r\\n'+\n'--------------------------1234567890987654--\\r\\n';\n\n\nlet mymsg1={};\n\nmymsg1.headers = {\n \"Content-Type\": \"multipart/form-data\"\n}\n\nmymsg1.payload = \n{\n \"imagefile\": {\n \"value\": msg.payload,\n \"options\": {\n \"filename\": \"FILENAME\"\n }\n }\n};\n\nreturn(mymsg1);\n",
"outputs": 1,
"noerr": 0,
"initialize": "",
"finalize": "",
"x": 490,
"y": 120,
"wires": [
[
"b29cd1d6.842e9"
]
]
},
{
"id": "b29cd1d6.842e9",
"type": "http request",
"z": "46a5b4e9.1028e4",
"name": "",
"method": "POST",
"ret": "txt",
"paytoqs": "ignore",
"url": "localhost:1880/cgi-bin/imgpost01.cgi",
"tls": "",
"persist": false,
"proxy": "",
"authType": "",
"x": 690,
"y": 120,
"wires": [
[]
]
},
{
"id": "99b3be79.f601c8",
"type": "change",
"z": "46a5b4e9.1028e4",
"name": "pick file",
"rules": [
{
"t": "move",
"p": "req.files[0].buffer",
"pt": "msg",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 310,
"y": 120,
"wires": [
[
"682c93b2.3ecacc",
"a06d87ec.8581c8"
]
]
},
{
"id": "682c93b2.3ecacc",
"type": "file",
"z": "46a5b4e9.1028e4",
"name": "",
"filename": "img01.jpg",
"appendNewline": true,
"createDir": false,
"overwriteFile": "true",
"encoding": "none",
"x": 480,
"y": 200,
"wires": [
[]
]
}
]
3.2. 各ノードの設定
では、左のノードから設定を説明します。
3.2.1. http inノード
http inノードを編集します。メソッドを POST にして、ファイルのアップロードにチェックを入れます。今回は、カメラノードから送付するURLは、mypost01という名前にしてあります。ここは適宜変更してください(カメラノードのプログラムと一致させます)。
3.2.2. Templateノード
postした側への返答をテンプレートノードで作成します。文面は何でも良いです。
3.2.3. HTTP-Responseノード
コード200を返すのがhttp responseです。特に何も設定する必要がありません。
3.2.4. changeノード
ここからが、中継としての処理になります。
changeノードで、http inで受け取った画像ファイルを取り出し、このデータ列をpayloadとして後続のノードへ渡します。
なお、このchangeノードの役割が、http postで受け取ったファイルの取り扱いとして重要です。
http-inノードは、POSTメソッドでファイルを受信すると、ファイルpayloadではなく、req.files[]にその情報が入ります。ファイル自体は、bufferに配列として格納されます。
フローよりもプログラム志向な方は、このchangeノードと等価な動きをする、次のスクリプトをfunctionノードで書いた方が馴染みやすいかもしれません。
let mymsg={};
mymsg.payload=msg.req.files[0].buffer;
return mymsg;
3.2.6. Functionノード
今回の肝、functionノードでファイルを指定された構造で包みます。 http-responseのヘルプに書いてある内容そのままです。
内容は次の通りです。相手のCGIで抽出する名前とキー名を一致させる必要があります。今回は、"imagefile"
としています。なお、参考にしたのは次のページです。 https://discourse.nodered.org/t/http-post-image-image/32669/2
let mymsg1={};
mymsg1.headers = {
"Content-Type": "multipart/form-data"
}
mymsg1.payload =
{
"imagefile": {
"value": msg.payload,
"options": {
"filename": "FILENAME"
}
}
};
return(mymsg1);
3.2.7. HTTP-Requestノード
最後に、http requestノードで、payloadに画像とともに載せた情報をサーバに渡します。サーバのURLを記述します。ここでは、サンプルとして、このホスト内のサービスに投げています。
4. 重要な点
node-redで画像をhttp postで受信し、http postで送信する場合の重要な点を挙げます。
- 送信では送信内容を指定された構造で包みます。
- バイナリのまま送信できます。(Base64にする必要がありません)
- 受信のファイルは、
msg.req.files[0].buffer
に格納されています。
これらを知っていれば、戸惑う事は無いと思います。
5. おわりに
今回は、Node-Redで画像をhttp postで中継送信するためのフローを挙げました。 冒頭でM5Cameraは能力的に不安があるという書き方をしましたが、実際は問題無いと思います。しかし、開発工数的には、圧倒的にラズパイの方に歩があると思います。
これを模索するために、色々と試しましたので、それについても記事として上げました。