[GoogleCloudStorage] GCSのPythonAPIの使い方 [備忘]

# 1行で
- GCSのPythonAPI(`google-cloud-storage`)のよく使う処理をベタ書き&ラッパー関数としてここに作っておく

<font color="Gray">GCSをPythonで使うたびに公式ページでいろいろ調べに行っているのがあまりに効率悪いのですぐコピペできるようにQiitaに書いておく</font>

# 認証通す
`pip install google-cloud-storage`で事前にライブラリ自体はインストールしておく

また、以下の前提で話を進める

- Python>=3.5
- google-cloud-storage==1.23.0

## GoogleCloudPlatform上で実行
GCP上ならば別にクレデンシャル情報は不要

```python
from google.cloud import storage as gcs

project_id = "{ProjectName}"
client = gcs.Client(project_id)
```

## ローカルで実行
ローカルで実行するにはサービスアカウントのjsonファイルを手元に用意してパスを通しておく必要あり

やり方は[公式を参照](https://cloud.google.com/iam/docs/creating-managing-service-account-keys?hl=ja)

```python
from google.cloud import storage as gcs
from google.oauth2 import service_account

key_path = '{クレデンシャルを格納しているPath}'
credential = service_account.Credentials.from_service_account_file(key_path)

project_id = "{ProjectName}"
client = gcs.Client(project_id, credentials=credential)

```


# バケット一覧を表示する
Projectに存在するバケットがどんなものかを確認する

```python:バケットの一覧を表示
project_id = "{ProjectName}"
client = gcs.Client(project_id, credentials=credential)

[print(bucket.name) for bucket in client.list_buckets()]
```

ちなみにバケットは名称を指定してオブジェクトを取得できる

```python:バケットのオブジェクトを取得する
project_id = "{ProjectName}"
bucket_name = "{BucketName}"

client = gcs.Client(project_id)
bucket = client.get_bucket(bucket_name)
```

# ローカルファイルをUpload
ローカルにあるファイルをGCSの特定のパスにUploadする

## ファイルとしてUpload
まずは、普通にファイルをGCSにUploadするパターン

```python:ローカルファイルのパスを指定してGCSに格納
project_id = "{ProjectName}"
bucket_name = "{BucketName}"
gcs_path = "{GCSFilePath}"
local_path = "{LocalFilePath}"

client = gcs.Client(project_id)
bucket = client.get_bucket(bucket_name)
# 格納するGCSのPathを指定(/xxx/yyy.csv)的な
blob_gcs = bucket.blob(gcs_path)
# ローカルのファイルパスを指定
blob_gcs.upload_from_filename(local_path)
```

## pd.DataFrameとしてUpload
たまに面倒くさくなっていきなりDataFrameからCSVにシリアライズしてGCSに格納したくなるので書いておく

```python:DataFrameをいきなりGCSに入れちゃう
import pandas as pd

project_id = "{ProjectName}"
bucket_name = "{BucketName}"
gcs_path = "{GCSFilePath}"

client = gcs.Client(project_id)
bucket = client.get_bucket(bucket_name)

# 格納するGCSパスを指定
blob = bucket.blob(gcs_path)

df = pd.DataFrame([[1,2,3],[5,6,7]],columns=["x","y","z"])
# DataFrameをGCSにUpload(ヘッダーは入れるが、インデックスは落とす)
blob.upload_from_string(df.to_csv(index=flg_index, header=flg_header, sep=","))
```

# GCSのファイルをDownload
次にDownloadするパターン
ファイルとして落としてくる場合とDataFrameとして落としてくる場合をカバーしておく

## ファイルとしてDownload
まずはファイルとしてDownloadする場合

```python:GCSにあるファイルをローカルに落としてくる
project_id = "{ProjectName}"
bucket_name = "{BucketName}"
gcs_path = "{GCSFilePath}"
local_path = "{LocalPath}"

client = gcs.Client(project_id)
bucket = client.get_bucket(bucket_name)

# GCSにあるダウンロードしたいファイルを指定
blob = bucket.blob(gcs_path)
# ファイルとしてローカルに落とす
blob.download_to_filename(local_path)
```

## CSVをDataFrameとして受け取る
変則的だが、CSVファイルを直接pd.DataFrameとして受け取るパターンもよく使うから書いておく

```python:GCSにあるファイルをDataFrameとして受け取る
import pandas as pd

from io import BytesIO

project_id = "{ProjectName}"
bucket_name = "{BucketName}"
gcs_path = "{GCSFilePath}"

client = gcs.Client(project_id)
bucket = client.get_bucket(bucket_name)

# GCSにあるダウンロードしたいファイルを指定
blob = bucket.blob(gcs_path)
# DataFrameとして受け取る(ヘッダーありのCSVを想定)
df = pd.read_csv(BytesIO(blob.download_as_string()))
df.head()
```

# バケット内のファイル一覧を取得する
バケットにあるファイルの中身を確認することもそれなりにあるので、これも記載しておく

```python:バケットを指定してファイル一覧を表示
project_id = "{ProjectName}"
bucket_name = "{BucketName}"
client = gcs.Client(project_id)
bucket = client.get_bucket(bucket_name)

[print(file.name) for file in client.list_blobs(bucket_name)]
```

# 機能をクラスでまとめておく
ここまで書いた内容をいちいちコピペするのが面倒なのでクラスにしてまとめておく

```python:ラッパークラスを定義
import pandas as pd

from google.cloud import storage as gcs
from io import BytesIO


class GCSWrapper:
    def __init__(self, project_id, bucket_id):
        """GCSのラッパークラス
        Arguments:
            project_id {str} -- GoogleCloudPlatform Project ID
            bucket_id {str} -- GoogleCloudStorage Bucket ID
        """
        self._project_id = project_id
        self._bucket_id = bucket_id
        self._client = gcs.Client(project_id)
        self._bucket = self._client.get_bucket(self._bucket_id)

    def show_bucket_names(self):
        """バケット名の一覧を表示
        """
        [print(bucket.name) for bucket in self._client.list_buckets()]

    def show_file_names(self):
        """バケット内のファイル一覧を表示
        """
        [print(file.name) for file in self._client.list_blobs(self._bucket)]

    def upload_file(self, local_path, gcs_path):
        """GCSにローカルファイルをアップロード

        Arguments:
            local_path {str} -- local file path
            gcs_path {str} -- gcs file path
        """
        blob = self._bucket.blob(gcs_path)
        blob.upload_from_filename(local_path)

    def upload_file_as_dataframe(self, df, gcs_path, flg_index=False, flg_header=True):
        """GCSにpd.DataFrameをCSVとしてアップロード

        Arguments:
            df {pd.DataFrame} -- DataFrame for upload
            gcs_path {str} -- gcs file path

        Keyword Arguments:
            flg_index {bool} -- DataFrame index flg (default: {False})
            flg_header {bool} -- DataFrame header flg (default: {True})
        """
        blob = self._bucket.blob(gcs_path)
        blob.upload_from_string(df.to_csv(
            index=flg_index, header=flg_header, sep=","))

    def download_file(self, local_path, gcs_path):
        """GCSのファイルをファイルとしてダウンロード

        Arguments:
            local_path {str} -- local file path
            gcs_path {str} -- gcs file path
        """
        blob = self._bucket.blob(gcs_path)
        blob.download_to_filename(local_path)

    def download_file_as_dataframe(self, gcs_csv_path):
        """GCSのファイルをpd.DataFrameとしてダウンロード

        Arguments:
            gcs_csv_path {str} -- gcs file path (only csv file)

        Returns:
            [pd.DataFrame] -- csv data as pd.DataFrame
        """
        blob = self._bucket.blob(gcs_csv_path)
        content = blob.download_as_string()
        df = pd.read_csv(BytesIO(content))
        return df
```

おしまい

# 参考
[【GoogleCloudPlatform】pythonでGCSバケットのデータを読み出す/書き出す](https://qiita.com/Gri_Gra/items/8a0a35225058aca71dc8)

[API Reference](https://googleapis.dev/python/storage/latest/index.html)