Node.jsからCloud Storageを利用(入門)

# サービスアカウントの作成
[Google Cloud Platform](https://console.cloud.google.com/)にアクセスしてサービスアカウントの作成（API）を行います。
ナビゲーションメニュー > APIとサービス　> 認証情報 をクリックし画面に移動します。
そしてサービスアカウントを管理をクリックします。
![GCP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/18512082-f5d6-4f93-faa8-bb2ddaaa26be.png)

次の画面ではサービスアカウントを作成をクリックします。
![GCP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/252ccae4-220b-ad61-4b18-5971d51dc04a.png)
サービスアカウントの詳細を各項目入力していきます。

|設定項目  |設定内容  |
|---|---|
|サービス アカウント名|（任意の名前に設定） |
|サービスアカウント説明|（各プロジェクトわかりやすいように任意で設定） |
入力が終了したら作成をクリックします。
![スクリーンショット 2020-10-31 20.24.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/50d378e2-8357-8350-19da-50ee45ff25a5.png)
次の項目ではCloud Storageに関するロールを作ります。今回はテストなので`ストレージ管理者`(フル権限)を選択しました。
使用アプリケーションに応じてロールを変更するようにしましょう。
![スクリーンショット 2020-10-31 20.25.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/f4d3a526-3045-3605-5c04-6d4aaee98a49.png)

最後の項目は省略して大丈夫です。
作成したサービスアカウントをクリックし、キーの項目で鍵を追加をクリックし新しい鍵を作成を選択します。
![スクリーンショット 2020-10-31 20.00.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/d5523faa-98f7-7c18-74fc-f3467058f99e.png)

![GCp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/e79af6aa-2f4e-bafb-7517-290f0ee0a04d.png)

キーのタイプはJSONを選択して「作成」します。ローカルストレージ内にJSONファイルがダウンロードされるので次の項目でそのJSONファイルを使用し、PythonからCloud Storageを操作していきます。

# NodeからCloudStorageを操作
## Nodeライブラリーのインストール
今回のプロジェクトで必要なライブラリーを順番にインストールしていきます。

```
npm install @google-cloud/storage
```

Excelファイルをjavascriptで読み込みこむ為に、npmパッケージのxlsjsをインストールします。

```
npm i xlsx-populate --save
```

## Cloud Storage上のバケットの準備
GUIからGoogle Cloud Storageにアクセスし、バケットを作成し適当な画像ファイルを準備します。
コンソール画面から　ナビゲーションメニュー >　Cloud Storage　で移動します。
今回はPythonのロゴファイルをダウンロードし、image.pngという名前をつけ保存しました。
[Pythonのロゴファイル](https://www.python.org/static/img/python-logo@2x.png)
![スクリーンショット 2020-10-31 20.53.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/817075/66b167e6-2b9f-6067-412d-e7bb45bb5fd7.png)

## Node.jsからCloud Storageに画像ファイルを取得
GCS上のエクセルファイルを取得し、`createReadStream()`によりBlobデータをメモリー上に読み込みます。その後Node.jsのライブラリsharpによりローカル内にファイルを作成保存する流れになります。

```
const {Storage} = require('@google-cloud/storage');
const sharp = require('sharp');

const keyFilename = '{キーとなるJSONファイルのパス}';
const bucketName = '{任意の名前で作成したバケット名}';

// バケットの取得
const storage = new Storage({keyFilename: keyFilename});
const bucket = storage.bucket(bucketName);

const file = bucket.file('image.png')
file.download().then(async function(data) {
    const contents = data[0];
    sharp(contents).toFile('output.jpg', (error, info) => {
      if (error) throw error
      console.info(info)
    })
```

## Node.jsからCloud Storageにアクセスしエクセルファイルを取得
GCS上のエクセルファイルを取得し、`createReadStream()`によりBlobデータをメモリー上に読み込みます。その後XLSX-populate ライブラリによりデータオブジェクトからブックを生成し、エクセルファイルをローカル内に保存します。
<strong color="rgb(255,0,0)"><font color="Red">サイズの大きいファイルを読み込むとマシーンによってはメモリ不足になります。</font></strong>

```
const {Storage} = require('@google-cloud/storage');
const XlsxPopulate = require('xlsx-populate');

const keyFilename = '{キーとなるJSONファイルのパス}';
const bucketName = '{任意の名前で作成したバケット名}';

// バケットの取得
const storage = new Storage({keyFilename: keyFilename});
const bucket = storage.bucket(bucketName);

const file = bucket.file('test.xlsx')
file.download().then(async function(data) {
    const contents = data[0];
    XlsxPopulate.fromDataAsync(contents)
    .then(async function (workbook) {
        workbook.toFileAsync("./out.xlsx");
    });
});
```

## ExpressからExcelファイルをダウンロードさせる場合

```
const XlsxPopulate = require('xlsx-populate');
const {Storage} = require('@google-cloud/storage');

const express = require("express");
const app = express();

const keyFilename = '{キーとなるJSONファイルのパス}';
const bucketName = '{任意の名前で作成したバケット名}';

// バケットの取得
const storage = new Storage({keyFilename: keyFilename});
const bucket = storage.bucket(bucketName);

// -> Express RestAPIs
app.get("/api/download/excel", (req, res, next) => {
    const file = bucket.file('test.xlsx')
    file.download().then(async function(data) {
        const contents = data[0];
        XlsxPopulate.fromDataAsync(contents)
            .then(async (workbook) => {
            return await workbook.outputAsync();
        }).then(data => {
            console.log(data);
            // Set the output file name.
            res.attachment("output.xlsx");
            // Send the workbook.
            res.send(data);
        }).catch(next);
    });
});

// ポート3000でサーバを立てる
const server = app.listen(5000, () => {
    console.log("Node.js is listening to PORT:" + server.address().port);
});
```

# 参考にさせていただいた記事
・[nodeでGCPのCloud Storageの操作 - iMind Developers Blog](https://blog.imind.jp/entry/2019/11/16/122422)
・[node.jsで（はうまくいかなかったのでpythonで）、gcsにあるサイズの大きいjsonlファイルを、mongodbに登録する（メモリふっとばさずに） - Qiita](https://qiita.com/seesaa10/items/49ac690bf4b4abab7e82)
・[ExcelをNode.js(javascript)で読み込む方法。xlsxについて解説します。|おちゃカメラ。](https://photo-tea.com/p/17/excel-parse-javascript-node/)
・[【Node】エクセルの中身を読み込む - なんだかGoodVibes](https://nandakagoodvibes.hatenablog.com/entry/2019/12/07/193402)
・[Node.js - Node.jsでbufferに保存されたExcelをコード上で扱いたい｜teratail](https://teratail.com/questions/283059)
・[Node.jsのライブラリsharpでEXIFなどの画像情報を取得してみる | Simple is Beautiful.](https://blog.kozakana.net/2019/04/sharp-get-information/)