GCP: Google Cloud Functions から Google Cloud Storage のファイルを読み込む

# 参考
公式ドキュメントだとStorageにファイル作成する例しかなかったので、こちらのQiita記事を参考にさせていただきました。
ありがとうございます！
[Cloud Storage Client Libraryを使ってGCS上のファイルをReadする - Qiita](https://qiita.com/t-yotsu/items/0ebc5d8aa18f8175c243)

# 概要
Google Cloud Functions (Java11) から Google Cloud Storage 上のファイルを読み込む。

- ファイルの内容
    - テキストファイル
    - 1行1データ
    - 改行コードはLF
    - 日本語含む
    - UTF-8

# やったこと

## Storageにバケットを作って、ファイルを置く

バケットはStorageの管理単位のイメージです。

GCPコンソール > Storage メニューから「+バケットを作成」します。
以下を設定していきます。

- バケット名
    - バケット名は一般公開されます
    - そのため機密情報や既に使われている値は利用できません
    - [命名ガイドライン](https://cloud.google.com/storage/docs/naming-buckets)
- データの保存場所
    - [バケットのロケーション](https://cloud.google.com/storage/docs/locations)
- データのデフォルトのストレージ クラス
    - [ストレージクラス](https://cloud.google.com/storage/docs/storage-classes)
- オブジェクトへのアクセスを制御する方法
    - 「きめ細かい管理」か「均一」のいずれかを選択します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628295/efdd7d48-f2ae-e02f-818c-65bb9114b96f.png)

作成したバケットの詳細を開き、「ファイルをアップロード」からファイルをアップします。
公開アクセスは「非公開」になっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628295/6e7024d3-b620-b3c8-1535-871e5f8634c9.png)

## Functionsを作る

GCPコンソール > Cloud Functions メニューから「+関数を作成」します。
関数名、リージョンは適宜設定します。

検証しやすいので、トリガーは「HTTP」にしました。
認証も「未認証の呼び出しを許可」にしておきます。

構成を決めたらコードを用意します。
ランタイムは「Java11」を選択して、サンプルソースを下記のように書き換えます。

```Example.java
package com.example;

import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.cloud.storage.*;
import java.io.UnsupportedEncodingException;
import java.io.BufferedWriter;

public class Example implements HttpFunction {
  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    response.setContentType("Content-Type: text/plain; charset=UTF-8");
    BufferedWriter writer = response.getWriter();

    String name = "";
    if (!request.getFirstQueryParameter("name").isEmpty()) {
      name = request.getFirstQueryParameter("name").get();
    }
    if(name.isEmpty()){
      writer.write("Query parameter 'name' is empty");
      return;
    }

    String[] datas = getBlobData("XXXXXX_projectId", "XXXXXX_bucketName", name);
    for(String data : datas){
      writer.write(String.format("'%s'%n", data));
    }
  }

  public String[] getBlobData(String projectId, String bucketName, String blobName) {
    Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
    BlobId blobId = BlobId.of(bucketName, blobName);
    Blob blob = storage.get(blobId);
    byte[] content = blob.getContent();
    try {
      return new String(content, "UTF-8").split("\n", 0);
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
    return new String[0];
  }
}

```

pom.xmlは下記を追加します。

```pom.xml
<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-storage</artifactId>
    <version>1.113.4</version>
</dependency>
```

最後に「デプロイ」してエラーがなければOK！

## 挙動確認
FunctionsのトリガーURLを呼び出します。
Chromeであればそのままbodyの内容が表示されるので、Storageに置いたファイルの内容が表示されれば成功です。

name=test.txtにしたとき
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628295/10fd8cfd-6042-eca5-9faa-ea18231548fd.png)

nameを空にしたとき
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628295/7ee987c0-e88d-1946-ae97-5b3cf7bf508f.png)

# 感想

同じGCPプロジェクトの Functions から Storage なら、認証なしで参照できて便利でした。

ただ Functions で、しかもJavaでやる処理じゃない気がしています(とても遅い)。
そのうちGAEに乗せ換えるか、Javaから他の言語にする方がいいかなと感じてはいます。