AzureにNode.jsから画像を保存したりDLしたりする


何かと画像を保存したいってシーンは出てきますよね。

[AzureにNode.jsから画像を保存する 準備編](https://qiita.com/n0bisuke/items/fb422677505da6c0997d)の続きですが、Azure Blob Storageに画像をアップロード、逆にダウンロードしてローカルに保存を試してみます。

とりあえず、Blob Storage内での概念として、`コンテナ=フォルダ的な概念`、`blob=ファイル的な概念でコンテナ内に作成される`と現状思っています。

## 画像用意など

まずは利用する画像を用意してみました。`n0bisuke.jpg`

> ![スクリーンショット 2021-09-06 10.22.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/aa5476e7-16b9-e5fa-d05c-f2eccaf36086.png "スクリーンショット 2021-09-06 10.22.47.png")

前回試したコードで`hello`という文字列を送っていたので、そのコードをそのまま使ってbase64変換して送ってみます。

## 画像をアップロード

```uploadimage.js
'use strict';

const { BlobServiceClient, StorageSharedKeyCredential, ContainerClient } = require("@azure/storage-blob");

//ファイルを利用
const fs = require(`fs`);
const FILE_PATH = `n0bisuke.jpg`;

//画像ファイルをbase64文字列へ
const image_data = fs.readFileSync(FILE_PATH);
const base64_data = "data:image/jpeg;base64," + image_data.toString('base64');

async function main() {
  // Enter your storage account name and shared key
  const account = process.env.ACCOUNT_NAME || "ストレージアカウント名";
  const accountKey = process.env.ACCOUNT_KEY || "キー";

  const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey);
  const blobServiceClient = new BlobServiceClient(
    `https://${account}.blob.core.windows.net`,
    sharedKeyCredential
  );

  //コンテナを作成した名前に指定
  const containerName = `n0bisuke-container1630888214169`;
  const containerClient = blobServiceClient.getContainerClient(containerName);
  
  // Create a blob
  const content = base64_data; //変換したbase64文字列を使う
  const blobName = "newblob" + new Date().getTime();
  const blockBlobClient = containerClient.getBlockBlobClient(blobName);
  const uploadBlobResponse = await blockBlobClient.upload(content, Buffer.byteLength(content));
  console.log(`Upload block blob ${blobName} successfully`, uploadBlobResponse.requestId);
}

main().catch((err) => console.error("Error running sample:", err.message));
```

```bash
$ node uploadimage.js 
Upload block blob newblob1630891763317 successfully 37db3c29-201e-004e-52c6-a28313000000
```

`newblob1630894950109`というblob名でアップロードされました。


ポータル上でも確認できました。6.93KiBということでしっかり保存されてそうです。

> ![スクリーンショット 2021-09-06 11.29.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/2cd700d3-dfc5-c128-37aa-e2fed20ed7bb.png "スクリーンショット 2021-09-06 11.29.23.png")

## 画像をダウンロードして保存

今後は逆にDLを試してみます。テキストだけでは仕方ないので画像ファイルとして保存する処理まで書いていきます。

> 参考: [Node.jsで画像をダウンロードして保存する（axios利用）](https://qiita.com/n0bisuke/items/26ce42781f00bb43beec)
> 参考: [[Node.js] Base64エンコードされたファイルデータをデコードして、S3にputObjectする](https://dev.classmethod.jp/articles/node-js-base64-encoded-image-to-s3/)

この辺りが参考になりました。

先程アップロードした`newblob1630891763317`のblob名を直指定で使います。

[公式サンプルにある`streamToBuffer()`](https://github.com/Azure/azure-sdk-for-js/blob/main/sdk/storage/storage-blob/samples/javascript/basic.js#L90-L101)を利用して文字列にエンコードして、base64文字列からデータの中身を取り出して`writeFileSync()`でファイル保存しています。

```dl.js

'use strict';

const { BlobServiceClient, StorageSharedKeyCredential, ContainerClient } = require("@azure/storage-blob");

const fs = require(`fs`);
const FILE_PATH = `n0bisuke.jpg`;

async function main() {
  // Enter your storage account name and shared key
  const account = process.env.ACCOUNT_NAME || "ストレージアカウント名";
  const accountKey = process.env.ACCOUNT_KEY || "キー";

  const sharedKeyCredential = new StorageSharedKeyCredential(account, accountKey);
  const blobServiceClient = new BlobServiceClient(
    `https://${account}.blob.core.windows.net`,
    sharedKeyCredential
  );

  //コンテナを作成した名前に指定
  const containerName = `n0bisuke-container1630888214169`;
  const containerClient = blobServiceClient.getContainerClient(containerName);
  
  //blobにアクセス
  const targetBlobName = `newblob1630891763317`;
  const blockBlobClient = containerClient.getBlockBlobClient(targetBlobName);

  //blobからデータをダウンロード
  const downloadBlockBlobResponse = await blockBlobClient.download(0);

  //データを文字列に
  const encodedData = (await streamToBuffer(downloadBlockBlobResponse.readableStreamBody)).toString();
  
  //今回は元が画像ファイルだったのでbase64形式のテキストがDLされるので中身を抽出
  const fileData = encodedData.replace(/^data:\w+\/\w+;base64,/, '');

  //ローカルファイルに書き込み
  fs.writeFileSync(`./dl/${FILE_PATH}`, fileData, 'base64');
}

// A helper method used to read a Node.js readable stream into a Buffer
async function streamToBuffer(readableStream) {
    return new Promise((resolve, reject) => {
      const chunks = [];
      readableStream.on("data", (data) => {
        chunks.push(data instanceof Buffer ? data : Buffer.from(data));
      });
      readableStream.on("end", () => {
        resolve(Buffer.concat(chunks));
      });
      readableStream.on("error", reject);
    });
}

main().catch((err) => console.error("Error running sample:", err.message));
```

実行すると...
無事に保存された！

> ![スクリーンショット 2021-09-06 11.41.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/9ca8a1d5-8349-8d43-e4f3-4dd63f03be18.png "スクリーンショット 2021-09-06 11.41.58.png")

`encodedData.replace(/^data:\w+\/\w+;base64,/, '');`の処理を忘れると、うまくファイルが表示出来なくなってしまうので注意って感じでした。

## まとめ

"画像をAzure Blob Storageにアップロードとダウンロードする"をNode.jsから試してみました。

終わってみるとあっさりでしたが、[最初](https://qiita.com/n0bisuke/items/fb422677505da6c0997d)が発狂しそうでした。笑