Azure Functions + node.js で Multipart/Post ファイルアップロード

流行りの「日本のITエンジニアが書いた」「やってみた」「個人メモ」な投稿です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/b3ea8a07-9d2b-c6d7-43f8-918f34a90c16.png)

こういうファイルアップロード用のフォームで、ファイルのアップロードを受け付ける機能を、Azure Functions + node.js で作ります。
このような機能の場合、multipart に対応しないといけないのがポイントです。

今回は multipart 対応に、[multer](https://github.com/expressjs/multer) という npm パッケージを使います。

とりあえず Azure ポータルで、Functions App を node.js で作り、次のように ``package.json`` ファイルを配置します。

**package.json**

```json
{
  "name": "uploader",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "memory-streams": "^0.1.3",
    "multer": "^1.4.0"
  }
}
```

次に ``index.js`` を次のように書き換えます。

**index.js**

```javascript
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const streams = require('memory-streams');
const upload = multer({ storage: multer.memoryStorage() });

module.exports = function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');
    const stream = new streams.ReadableStream(req.body); 
    for (const key in req) {
        if (req.hasOwnProperty(key)) {
            stream[key] = req[key];
        }
    }
    context.stream = stream;

    upload.any()(stream, null, (err) => {
        const f = context.stream.files[0]
        const p = path.join(__dirname, `./${f.originalname}`);
        fs.writeFileSync(p, f.buffer);
        context.res = { body: `Upload ${f.originalname} done.` };
        context.done();
    });
};
```

**package.json**

```json
{
  "name": "uploader",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "memory-streams": "^0.1.3",
    "multer": "^1.4.0"
  }
}
```

次に Azure Portal のコンソールで ``npm install`` を実行します。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/8b27da2a-5c1f-a935-df37-982853531b67.png)

サーバー側はこれで終わりなんで、「関数のURLを取得」しておきます。あ、メニューの 統合 の方で、「POST を許可する」のと「承認レベルを anonymous にする」のを忘れずに。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/ce8f7a62-09f2-63d3-fbe2-31761d7dc88d.png)


次にクライアント側をテキトーに作ります。

* [node.js express でWebサーバーを作り、ファイル/ディレクトリ一覧表示する - Qiita](https://qiita.com/standard-software/items/b0df2ca4640b31b710e4)

を参考にさせていただいて、 ``index.html`` を次のように記述してフォームを作ります。

```html
<!DOCTYPE html>
<html>
<body>
  <form method="post" action="https://xxxx.azurewebsites.net/api/HttpTriggerJS1" enctype="multipart/form-data">
    <input type="file" name="example1">
    <input type="submit" value="SEND">
  </form>
</body>
</html>
```

``action`` に指定するURLはさっきコピった Azure Function の関数のURLです。
できたら ``node server01.js`` で Webサーバを起動し、 ttp://localhost:8001 でフォームを表示します。
適当なファイルを選択して「SEND」ボタンを押せば、ファイルがアップロードされるはずです。
アップロードが完了するとページ遷移して「Upload xxxx.xxx done.」という表示になります。

さて、ファイルが実際にどこにアップロードされたかというと、Azure Functions が配置されたディレクトリに保存されています。

試しにフォームから ``input.png`` というファイルをアップロードしたあとで、Azure ポータルの方でコンソールを開いて ``ls -l`` を実行すると、下図のように ``input.png`` が存在していることがわかります、ファイルサイズもなんかそれっぽいので成功しているのでしょう。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8227/b9e93ece-8d6d-cccf-51df-ba5a51092829.png)

## 未確認なこと

* ちゃんとマルチパートな送信データを処理できるよね？ → フォームで送ったらパートが一つだけだったので未確認。
* 大きなファイルサイズ耐えられる？ → ダメかも。5MB程度のファイルをアップロードしたら 502 エラーになりますた。どこまで耐えられてどうしたら上限引き上げられるのかは未調査。