node-redで画像をhttp postする方法を対向部分も構築しながら模索した話

# 1. はじめに
今回は、Node-Redのhttp-requestノードを用いて、[画像をhttp postする方法](https://qiita.com/dzonesasaki/items/ec08981a18c0740f426e)を理解するまでに模索した話を記載します。
目的の送信フローだけでなく、Node-Red内で対向側（受信側）も構築して試しました。Node-Redでどうやって目的の機能を構築していくか？という流れとしてのメモでもあります。

# 2. 前提

今回の想定しているのは、カメラノードからhttp post でアップロードされた画像をラズパイが一旦引き取り、クラウド側へhttp postでアップロードするという構成です。図にすると次のような構成です。
![diagramOverall.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/e68f2e10-dfeb-4686-1a2f-f2168f3179ce.png)

http inノードを使った、http postでファイル受信は、[確立できている](https://qiita.com/dzonesasaki/items/88deb9a2acb8be2d7cfb)ので、http requestノードでhttp postのファイル送信を確立することを目指します。

# 3. 利用したフロー

## 3.1. 全体

次が、今回、模索しながら作ったフローです。
全部で3つあります。

- 一番上が、webブラウザでhttp postする実験用フロー。
- 真ん中が、http postを受信するフロー。
- 一番下が、今回の目的、画像をhttp postを試したフロー。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/835e5bf4-28e8-8199-2e6e-4d75734caa51.png)

コード(json)は最後に記します。
このフローを試すにあたって、node-red-node-base64とnode-red-contrib-image-toolsをインストールしました。
（ 「パレットの管理」から 「ノードの追加」 を選び、上記名称を入力してインストールします）

## 3.2. ブラウザ用フロー

http inでブラウザからの要求を受け付け、htmlファイルの内容をそのまま返します。

### 3.2.1. http inノード (get)

普通にgetメソッドです。URLを適当に付けます。このURLをブラウザで叩きます。

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/bb93f32f-0a81-a502-53a2-b89dc340c0cc.png)

### 3.2.2 Templateノード (ブラウザ表示用html)

cgiにpostするためのhtmlをここで作成しました。 cgiのURLは、後のノードのURLと一致させます。
参考にしたのは、こちらです： https://masublog.net/python/pythoncgiimageuploader/
この内容を検証するには、webサーバを立ち上げて、そこにhtmlファイルを置く必要がありますが、このようにnode-redで簡単に代用できます。

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/4fed08af-367b-a446-3721-2e77ff1cd745.png)

```html
<!DOCTYPE html>
<html lang="ja" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>test</title>
  </head>
  <body>
    <form action="/cgi-bin/imgpost01.cgi" method="post" enctype="multipart/form-data">
            <input type="file" name="imagefile">
            <input type="submit">
    </form>
  </body>
</html>
```


### 3.2.3 http responseノード

このノードは、返答するもので、特に編集することなく、置くだけで機能します。

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/0498b042-1bd2-ebec-1d61-e6c6b99832f8.png)


## 3.3. http postを受信するフロー

http postを受信するフローは、本来はサーバ側でCGIとして機能する部分をnode-redで検証するために構築したものです。
CGIの機能として参考にしたのは、先ほども挙げましたが、こちらです： https://masublog.net/python/pythoncgiimageuploader/ 


### 3.3.1. http in ノード (POST)

メソッドを **POST** にして、**ファイルのアップロード**にチェックを入れます。 URLは、CGIが機能するであろうURLにしています。 典型的な cgi-binにcgiのスクリプトを置くパターンを想定しています。

![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/c15d500d-a058-e478-d874-d740f9e7adcf.png)

### 3.3.2. Templateノード（post確認用)

postした側への返答をテンプレートノードで作成します。文面は何でも良いです。

![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/e96f59ca-8e96-3765-f47b-595cbfcaccf7.png)

### 3.3.3. http responseノード

postした側へ返答機能のノードです。置くだけで機能します。前述してあるので、図は省略します。

### 3.3.4. changeノード 

changeノードで、http inで受け取った画像ファイルを取り出し、このデータ列をpayloadに置き直し後続のノードへ渡します。

![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/a6d212d2-0a91-65c3-2e1a-fa068616317c.png)

http inノードが、POSTメソッドでファイルを正しく受信すると、msg.req.files[0].bufferに格納されます。
このchangeノードと等価な動きをする、スクリプトは次です。functionノードで置き換えても同じです。

```javascript
let mymsg={};
mymsg.payload=msg.req.files[0].buffer;
return mymsg;
```

送信側のデバッグで重要なのが、msg.req.files[0].bufferに何が格納されているかです。
デバッグノードを付けた場合は、デフォルトの```msg.payload```を表示するのではなく、```msg```全体に変更します。（後述します）


### 3.3.5. base64ノード

画像表示ノードでは、base64にエンコードした画像が必要なようでした。（他の方法は分かりませんでした）
そこで、base64ノードを追加して、msg.payloadのバイナリーデータをbase64に置き換えます。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/d518a075-c57d-9086-9333-9c8abf5725d7.png)

もし、base64ノードをインストールしたくない場合は、次のコード(functonノードに記載)で代用可能です。

```javascript
msg.payload = msg.payload.toString('base64');
return msg;
```


### 3.3.6. functionノード (画像表示用ヘッダの追加)

後の画像表示ノードに渡すために、データの先頭に```"data:image/jpeg;base64,"```という文字を追加します。これが無いと、画像表示ノードで画像が表示してくれませんでした。

![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/6f3b0a8a-8250-01c7-5351-23ed51da0fef.png)


### 3.3.7. 画像表示ノード

これは、フロー上に流れる画像を簡易的に表示してデバッグしやすいようにするためのものです。msg.payloadにあるデータが、画像データとしての体をなしていれば、表示されます。

![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/4528bf4b-7c5f-91d2-e881-87b2c6758934.png)

なお、もう一つ、画像表示の流れがありますが、これは、base64の状態でファイルが保存された場合に表示されるようにしたものです。


## 3.4. 画像をhttp postを試すフロー

ここからが、今回の目的であるhttp postで画像を送るフローです。

### 3.4.1. injectionノード

injectionノードは、実行トリガとなる、いわばrunボタンみたいなものです。実行と同時に、msg.payloadに流し込む情報も定義できます。デフォルトでは、日時となっているので、そこをクリックし、文字列を選択します。ここに画像ファイルをbase64に変換したものを貼り付けました。


![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/bbebe2a8-bcd5-94b2-6723-628efd6fc7b0.png)

貼り付けた画像はこちらです。
![10x10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/a9bbbce2-d578-c203-7771-ff0291356152.jpeg)
base64に変換した文字列は後述します。



### 3.4.2. functionノード(ファイル包み込み)

今回のhttp postで画像を送信する方法の解が、このノードです。

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/1c38db8b-5147-a321-f67a-00f4b369dccc.png)

この方法は、http requestのヘルプに記載されています。

![25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/f44f8699-e6d5-0de9-c947-3b5c5ced9d26.png)

これを使えば、簡単にhttp postでファイルを送信できる訳です。
キー名は、cgiで抽出する```"imagefile"```というキーと一致させておきます。ファイル名はcgiで見ないので適当です。injectノードでbase64になっている文字列がmsg.payloadに乗って来るので、バイナリに戻して、格納します。

```javascript
let mymsg={};
let mybin = Buffer(msg.payload,'base64');

mymsg.headers = {
    "Content-Type": "multipart/form-data"
}


mymsg.payload=
{
    "imagefile":{
        "value": mybin,
        "options":{
            "filename":"10x10.jpg"
        }
    }
};


return(mymsg);
```

### 3.4.3. http requestノード

ここで、cgiにポストします。URLは今回、node-red上に作った疑似的な受付に対して送信します。うまくいったら、本番のCGIのURLを記述します。

![13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/c0115a4a-49e3-d05a-a839-42dbca84cbd9.png)


### 3.4.4. functionノード(マルチパート作成)

この部分は余計な部分です。今回、この検討で、このような回り道をしました。googleったら、次のページが出てきたので参考にして、base64のマルチパートを作成しました。 https://discourse.nodered.org/t/http-post-image-image/32669/2
そのページの最後に、「これで出来た」という内容が前述のファイル包み込みでした。しかし、これがmsg.payloadに入れるオブジェクトだとは理解できませんでした。そこで、そのページの1つ手前のマルチパート化を試しました。確かにこれで上手くいきます。この検討の最終的な[記事](https://qiita.com/dzonesasaki/items/ec08981a18c0740f426e)でも、当初、この方法を載せていました（変更履歴を見ると載っています）。こんな回りくどい方法をするのはおかしいと思いつつも、これが限界なのかな？と思いながらやっていました。その後も「これで出来た」の意味は何だったのかを、ずっと考えていました。
ところで、面白いのは、node-redでも、このような方法が有効だということです。node-redは自由度が高いということだと感じます。

![14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/d0ec9cca-0266-d4ea-88bb-5e37119a7cb9.png)


```javascript
let mymsg2={};

mymsg2.headers = {
    "Content-Type": "multipart/form-data; boundary=------------------------1234567890987"
}

mymsg2.payload = '--------------------------1234567890987\r\n'+
'Content-Disposition: form-data; name="imagefile"; filename="10x10.jpg"\r\n'+
'Content-Type: image/jpeg\r\n'+
'Content-Transfer-Encoding: base64\r\n'+
'\r\n'+
msg.payload +
'\r\n'+
'--------------------------1234567890987--\r\n';

return(mymsg2);

```

# 4. 動作確認

これまでのフローは試行錯誤しながら出来上がったものですが、まずは初めに何を行ったのか、述べていきたいと思います。

まずは、CGIのサンプルとしていた内容の確認でした。 https://masublog.net/python/pythoncgiimageuploader/ 
実際は、apacheを動かした仮想マシンでその記事と同じことを行いました。当然動くので、次の段階として、node-red上に、同じようなものを構築しておこうとなった訳です。記事では、index.htmlですが、判り易いようにimgpost.htmlに書き換えました。
ブラウザで、このnode-redが動いているマシン（ラズパイ）のIPアドレスと、ポート番号である1880を入れて、imgpost.htmlとしてアクセスします。するとブラウザ上では次のように表示されます。これは、apacheで試した応答と同じです。

![15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/3251d5b5-af77-cb2d-246f-87afc47d20a1.png)

画像ファイルを選び、送信ボタンを押すと次のようになります。この文字はtemplateノードに記載した内容です。

![16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/eb88cc87-cb78-94e9-ab6e-39334036e5b7.png)

ここでnode-redに戻ると次のように表示されています。受信した画像が表示されています。これで、htmlに記述したブラウザでのhttp postが受信できていることが判りました。これを動作の目標とします。
つまり、ここに画像が表示されれば、正しくhttp postが出来ている

![17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/f065e69f-2090-0091-6f64-a5a8eb3a10a2.png)

次に、injectノードの左側のボタンを押します。すると、次のようになります。このようになれば、このフローが正しいということになります。
これが完成形です。

![18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/017f2518-007c-9ace-16d3-2f714139a99a.png)

ここにいたるまでに、どのように作業したか？が、重要だと思っています。

まずは、送信側のデバッグで重要なのが、受信後のmsg.req.files[0].bufferに何が格納されているかです。
確認するには、http inの後にデバッグノードを付け、デフォルトの```msg.payload```を表示するのではなく、```msg```全体に変更します。

![19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/e2a072eb-ac85-b799-f0d5-9bdb4697ddc2.png)

http postを受信するとデバッグ側に次のように表示されますので、 三角マークをクリックします。

![20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/112d2544-b85b-b1a1-cc05-c2e6088a09e5.png)

msgの内容が展開表示されます。reqの部分の三角マークをクリックし、ずっと下を見ていきます。

![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/701ed7d8-40e4-581a-7f10-1017ea86ff2a.png)

http postでファイル送信がうまくいっている場合は、このように、filesの配列に内容が入っています。bufferに、送信した内容が格納されているはずです。

![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/28c92683-50ca-fbcc-f4a3-c4d1676aaf76.png)

まず、ファイルをそのままmsg.payloadに入れたまま、http requestに突っ込むと、このfilesにemptyと表示されます。受信側でファイルを抽出できてないというのがそこで判ります。なので、filesに配列が保存されるようにするには、どのように送信するのかを検討します。ここで参考になるのが、ブラウザでのhttp postになります。ブラウザで行った場合と、injectで行った場合で、このfilesのbufferに差が無ければ完了です。

そこで、http postでファイル送信がうまくいっているかどうかについて、bufferに格納されている値が、ファイルの内容と一致しているかを比較します。

![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/d527774a-5c2f-1bbd-fe25-a5a1126ed52b.png)

画像 
![10x10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/a9bbbce2-d578-c203-7771-ff0291356152.jpeg) の内容をバイナリエディタで見てみると次のようになります。(この画面はmacのhex fiendというアプリを使いました)

![24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698531/a34bb15f-f312-58b2-1fa2-cc41adb05eea.png)

先頭の8バイトは同じになっているのが判ります。

なお、画像をbase64に変換した文字列は次のようになっています。injectionノードで、この文字列を入れます。（変換してくれるサイトもありますし、ツールもあります。macでは、base64 ファイル名 で変換した文字を表示してくれます。） 先頭にスラッシュがありますので、はじめの1バイト目が、0x2F(47)になります。これはこれで、このbase64の文字列が送信されたことを知る手がかりになります。

```
/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/4gKwSUNDX1BST0ZJTEUAAQEAAAKgbGNtcwQwAABtbnRyUkdCIFhZWiAH5AAJABMAEwAhABRhY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEL/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////////////////////////////////////2wBDAVVaWnhpeOuCguv/////////////////////////////////////////////////////////////////////////wgARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAB/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQIA/9oADAMBAAIQAxAAAAFVehbMn//EABcQAAMBAAAAAAAAAAAAAAAAAAABEDH/2gAIAQEAAQUCj0//xAAXEQEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/AWIr/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwIf/8QAFxABAQEBAAAAAAAAAAAAAAAAAREAEP/aAAgBAQABPyFW6vT/2gAMAwEAAgADAAAAEFtP/8QAGBEAAwEBAAAAAAAAAAAAAAAAAAEREDH/2gAIAQMBAT8QSUIOM//EABgRAQADAQAAAAAAAAAAAAAAAAEAEBEx/9oACAECAQE/EAMmEOV//8QAFxABAAMAAAAAAAAAAAAAAAAAABAxYf/aAAgBAQABPxAGaiP/2Q==
```

このような流れで、http postをどう使うのかを模索していきました。


# 5. コード

<details><summary>testFlow.json</summary><div>

```json
[
    {
        "id": "9aa56ce6.4b64a8",
        "type": "function",
        "z": "8c360add.dc661",
        "name": "pick a file",
        "func": "let mymsg={};\nmymsg.payload=msg.req.files[0].buffer;\nreturn [mymsg];",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 400,
        "y": 220,
        "wires": [
            []
        ]
    },
    {
        "id": "b5fd8354.d3d2f8",
        "type": "http in",
        "z": "8c360add.dc661",
        "name": "",
        "url": "/cgi-bin/imgpost01.cgi",
        "method": "post",
        "upload": true,
        "swaggerDoc": "",
        "x": 160,
        "y": 180,
        "wires": [
            [
                "e30da6ff.9c7f3",
                "9aa56ce6.4b64a8",
                "fcfa114.88b217",
                "e621ccc5.3920e"
            ]
        ]
    },
    {
        "id": "4d19eb0f.af19fc",
        "type": "base64",
        "z": "8c360add.dc661",
        "name": "",
        "action": "",
        "property": "payload",
        "x": 540,
        "y": 260,
        "wires": [
            [
                "77b836f5.3be908"
            ]
        ]
    },
    {
        "id": "e30da6ff.9c7f3",
        "type": "template",
        "z": "8c360add.dc661",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "receved",
        "output": "str",
        "x": 400,
        "y": 140,
        "wires": [
            [
                "d2f81708.ffb88"
            ]
        ]
    },
    {
        "id": "fcfa114.88b217",
        "type": "debug",
        "z": "8c360add.dc661",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 190,
        "y": 260,
        "wires": []
    },
    {
        "id": "d2f81708.ffb88",
        "type": "http response",
        "z": "8c360add.dc661",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 530,
        "y": 140,
        "wires": []
    },
    {
        "id": "e66dea8f.bb098",
        "type": "image viewer",
        "z": "8c360add.dc661",
        "name": "",
        "width": 160,
        "data": "payload",
        "dataType": "msg",
        "x": 830,
        "y": 260,
        "wires": [
            []
        ]
    },
    {
        "id": "d9758f51.e0bfd8",
        "type": "image viewer",
        "z": "8c360add.dc661",
        "name": "",
        "width": 160,
        "data": "payload",
        "dataType": "msg",
        "x": 710,
        "y": 180,
        "wires": [
            []
        ]
    },
    {
        "id": "45cb597a.2a67f",
        "type": "inject",
        "z": "8c360add.dc661",
        "name": "10x10jpg",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/4gKwSUNDX1BST0ZJTEUAAQEAAAKgbGNtcwQwAABtbnRyUkdCIFhZWiAH5AAJABMAEwAhABRhY3NwQVBQTAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWxjbXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1kZXNjAAABIAAAAEBjcHJ0AAABYAAAADZ3dHB0AAABmAAAABRjaGFkAAABrAAAACxyWFlaAAAB2AAAABRiWFlaAAAB7AAAABRnWFlaAAACAAAAABRyVFJDAAACFAAAACBnVFJDAAACFAAAACBiVFJDAAACFAAAACBjaHJtAAACNAAAACRkbW5kAAACWAAAACRkbWRkAAACfAAAACRtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACQAAAAcAEcASQBNAFAAIABiAHUAaQBsAHQALQBpAG4AIABzAFIARwBCbWx1YwAAAAAAAAABAAAADGVuVVMAAAAaAAAAHABQAHUAYgBsAGkAYwAgAEQAbwBtAGEAaQBuAABYWVogAAAAAAAA9tYAAQAAAADTLXNmMzIAAAAAAAEMQgAABd7///MlAAAHkwAA/ZD///uh///9ogAAA9wAAMBuWFlaIAAAAAAAAG+gAAA49QAAA5BYWVogAAAAAAAAJJ8AAA+EAAC2xFhZWiAAAAAAAABilwAAt4cAABjZcGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1ZAAAT0AAACltjaHJtAAAAAAADAAAAAKPXAABUfAAATM0AAJmaAAAmZwAAD1xtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAEcASQBNAFBtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEL/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////////////////////////////////////2wBDAVVaWnhpeOuCguv/////////////////////////////////////////////////////////////////////////wgARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAgAB/8QAFgEBAQEAAAAAAAAAAAAAAAAAAQIA/9oADAMBAAIQAxAAAAFVehbMn//EABcQAAMBAAAAAAAAAAAAAAAAAAABEDH/2gAIAQEAAQUCj0//xAAXEQEAAwAAAAAAAAAAAAAAAAAAARAR/9oACAEDAQE/AWIr/8QAFBEBAAAAAAAAAAAAAAAAAAAAIP/aAAgBAgEBPwEf/8QAFBABAAAAAAAAAAAAAAAAAAAAIP/aAAgBAQAGPwIf/8QAFxABAQEBAAAAAAAAAAAAAAAAAREAEP/aAAgBAQABPyFW6vT/2gAMAwEAAgADAAAAEFtP/8QAGBEAAwEBAAAAAAAAAAAAAAAAAAEREDH/2gAIAQMBAT8QSUIOM//EABgRAQADAQAAAAAAAAAAAAAAAAEAEBEx/9oACAECAQE/EAMmEOV//8QAFxABAAMAAAAAAAAAAAAAAAAAABAxYf/aAAgBAQABPxAGaiP/2Q==",
        "payloadType": "str",
        "x": 120,
        "y": 360,
        "wires": [
            [
                "d5672ca5.0cf158",
                "1522ab80.5a242d",
                "7ce3361b.8757c8"
            ]
        ]
    },
    {
        "id": "1ae2a2d5.a9b8e5",
        "type": "http request",
        "z": "8c360add.dc661",
        "name": "",
        "method": "POST",
        "ret": "txt",
        "paytoqs": "ignore",
        "url": "localhost:1880/cgi-bin/imgpost01.cgi",
        "tls": "",
        "persist": false,
        "proxy": "",
        "authType": "",
        "x": 590,
        "y": 360,
        "wires": [
            []
        ]
    },
    {
        "id": "1522ab80.5a242d",
        "type": "function",
        "z": "8c360add.dc661",
        "name": "multipart",
        "func": "let mymsg2={};\n\nmymsg2.headers = {\n    \"Content-Type\": \"multipart/form-data; boundary=------------------------1234567890987\"\n}\n\nmymsg2.payload = '--------------------------1234567890987\\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'--------------------------1234567890987--\\r\\n';\n\nreturn(mymsg2);\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 360,
        "y": 460,
        "wires": [
            []
        ]
    },
    {
        "id": "a961c831.a7c44",
        "type": "debug",
        "z": "8c360add.dc661",
        "name": "",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "true",
        "targetType": "full",
        "statusVal": "",
        "statusType": "auto",
        "x": 690,
        "y": 400,
        "wires": []
    },
    {
        "id": "d5672ca5.0cf158",
        "type": "function",
        "z": "8c360add.dc661",
        "name": "addHeader",
        "func": "let mymsg2={};\nmymsg2.payload=\"data:image/jpeg;base64,\"+msg.payload;\n\nreturn(mymsg2);\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 370,
        "y": 420,
        "wires": [
            [
                "54a51ba0.2b1ef4"
            ]
        ]
    },
    {
        "id": "54a51ba0.2b1ef4",
        "type": "image viewer",
        "z": "8c360add.dc661",
        "name": "",
        "width": 160,
        "data": "payload",
        "dataType": "msg",
        "x": 570,
        "y": 420,
        "wires": [
            []
        ]
    },
    {
        "id": "2e780f3b.fcdf58",
        "type": "http in",
        "z": "8c360add.dc661",
        "name": "",
        "url": "/imgpost.html",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 130,
        "y": 60,
        "wires": [
            [
                "4119746c.9fb114"
            ]
        ]
    },
    {
        "id": "4119746c.9fb114",
        "type": "template",
        "z": "8c360add.dc661",
        "name": "",
        "field": "payload",
        "fieldType": "msg",
        "format": "handlebars",
        "syntax": "mustache",
        "template": "<!DOCTYPE html>\n<html lang=\"ja\" dir=\"ltr\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>test</title>\n  </head>\n  <body>\n    <form action=\"/cgi-bin/imgpost01.cgi\" method=\"post\" enctype=\"multipart/form-data\">\n            <input type=\"file\" name=\"imagefile\">\n            <input type=\"submit\">\n    </form>\n    <BR><BR>\n    refer to https://masublog.net/python/pythoncgiimageuploader/\n    <BR>\n  </body>\n</html>",
        "output": "str",
        "x": 310,
        "y": 60,
        "wires": [
            [
                "bc5f91b0.250a28"
            ]
        ]
    },
    {
        "id": "bc5f91b0.250a28",
        "type": "http response",
        "z": "8c360add.dc661",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 450,
        "y": 60,
        "wires": []
    },
    {
        "id": "e621ccc5.3920e",
        "type": "change",
        "z": "8c360add.dc661",
        "name": "pick file",
        "rules": [
            {
                "t": "move",
                "p": "req.files[0].buffer",
                "pt": "msg",
                "to": "payload",
                "tot": "msg"
            }
        ],
        "action": "",
        "property": "",
        "from": "",
        "to": "",
        "reg": false,
        "x": 400,
        "y": 180,
        "wires": [
            [
                "4d19eb0f.af19fc",
                "f48d053.6c313f8"
            ]
        ]
    },
    {
        "id": "f48d053.6c313f8",
        "type": "function",
        "z": "8c360add.dc661",
        "name": "addHeader",
        "func": "let mymsg2={};\nmymsg2.payload=\"data:image/jpeg;base64,\"+msg.payload;\n\nreturn(mymsg2);\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 550,
        "y": 180,
        "wires": [
            [
                "d9758f51.e0bfd8"
            ]
        ]
    },
    {
        "id": "77b836f5.3be908",
        "type": "function",
        "z": "8c360add.dc661",
        "name": "addHeader",
        "func": "let mymsg2={};\nmymsg2.payload=\"data:image/jpeg;base64,\"+msg.payload;\n\nreturn(mymsg2);\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 690,
        "y": 260,
        "wires": [
            [
                "e66dea8f.bb098"
            ]
        ]
    },
    {
        "id": "7ce3361b.8757c8",
        "type": "function",
        "z": "8c360add.dc661",
        "name": "wrapper",
        "func": "let mymsg={};\nlet mybin = Buffer(msg.payload,'base64');\n\nmymsg.headers = {\n    \"Content-Type\": \"multipart/form-data\"\n}\n\n\nmymsg.payload=\n{\n    \"imagefile\":{\n        \"value\": mybin,\n        \"options\":{\n            \"filename\":\"10x10.jpg\"\n        }\n    }\n};\n\n\nreturn(mymsg);\n",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "x": 360,
        "y": 360,
        "wires": [
            [
                "1ae2a2d5.a9b8e5",
                "a961c831.a7c44"
            ]
        ]
    }
]
```

</div></details>

# 5. おわりに


node-redで画像をhttp postする方法を模索した内容を記載しました。
ファイルをhttp requestに繋いだらうまくいかない。。。。 「node-red "http request" post image file」 でgoogleっても、うまく見つけられない。。。。結論は、node-redのヘルプに書いてあった、というオチでした。
この記事が、同様の悩みを持った人の参考になればと思っています。