【PHP】Laravelを使ってGoogleドライブにファイルをアップロードしてみる

## 対象読者

- PHP/Laravelを使ってGoogleドライブにファイルをアップロードしたい人。

コード実装以外の部分（認証のための準備）でてこずる部分が結構あったため、メモとして保存。サンプルコードも用意しているので、手順通りに進めれば必ず同じ結果を得られるようになっています。

## 下準備

コードを実装する前に、Googleドライブを外部から利用するために事前にやっておかなければならない事がいくつかあるのでそちらから手をつけていきます。（APIの有効化や認証アカウントの作成など）


### Google Cloud Platformの設定
https://console.cloud.google.com/
![gcp_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/0a54f6d8-6ab2-1f0a-f4c4-60d2d6fa9e2c.jpeg)

まず最初にGoogle Cloud Platform（GCP）の設定を行います。

これまでGCPを利用した事の無い人はスタートガイドに従って利用を開始してください。（クレジットカードなどの情報を入力する必要がありますが、無料トライアル期間があるのでこの時点で勝手に課金される事はありません。）

参照記事: [これから始めるGCP（GCE） 安全に無料枠を使い倒せ](https://qiita.com/Brutus/items/22dfd31a681b67837a74)

#### 任意のプロジェクトを選択

今回はアカウント作成時にデフォルトで用意された「My First Project」を使用します。

#### Google Drive APIの有効化

![APIとサービス→ライブラリ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/debfa382-910b-102c-a987-7161da4544b5.png)
![Google Drive API.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/ba9ebf19-0e99-7a3c-9023-d996fba27160.png)

左サイドバーから「APIとサービス→ライブラリ」を選択。
各種Google APIが出てくるので、その中から「Google Drive API」を見つけ、有効化します。

#### サービスアカウントを作成

![認証情報.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/41cd06e8-745a-7ac9-cd6c-f02506e4ae69.png)

有効化に成功するとGoogle Drive APIの管理画面に飛ぶので、左サイドバーから「認証情報」を選択。
外部からGoogle Drive APIを使用する際、認証に必要な「サービスアカウント」の作成を行います。

参照記事: [GCP Service Accountを理解する](https://qiita.com/t-yotsu/items/5d3d36847fbc71b72b76)

![サービスアカウント作成_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/5299cdaa-b827-4584-723e-b1892fb1699b.jpeg)
![役割.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/08474a82-27c2-012d-a9fa-53e3a6fd03ab.png)

アカウント名や説明文を入力し、「作成」ボタンをクリック。
その後、アカウントにロール（役割）を付与していきます。（今回はテストなのでProject→オーナーを選択）

③は無視でOKなので、そのまま「完了」ボタンをクリックしてください。

![成功_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/db9c03b6-bebf-94bb-0c24-c83006ad8760.jpeg)

上手くいくとこんな感じでサービスアカウントが追加されています。

#### 秘密鍵（JSON）を作成

![json_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a83b674b-05d1-fb7b-2b3a-b661be52db8f.jpeg)

アカウント詳細画面を開くと、下の方に「鍵を追加」というメニューがあるので、そちらから秘密鍵（JSON）を作成します。

![jsonを保存_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/28d3573c-031f-3696-1554-3d2b2aa42104.jpeg)

後にこの秘密鍵を使ってアプリからサービスアカウントへの認証を行うので、ダウンロードして保管しておきましょう。

### Googleドライブの設定

https://drive.google.com/drive/u/0/my-drive

次にGoogleドライブの設定を行います。

#### フォルダを作成

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/f5c462b4-2881-1a35-6b66-c82bb6d72020.png)

テスト用に適当なフォルダを用意しましょう。

#### サービスアカウントを共同編集者に追加

![共有.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/533f0ff1-47ee-61a9-c31b-71c6a514d3a0.png)
![メルアド共有_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/04f9992f-d9ae-901f-d8f4-69544b32f0f9.jpeg)

先ほど作成したサービスアカウントがGoogleドライブ内のフォルダにアクセスできるよう、「共有」を選択してサービスアカウントのメールアドレスを追加します。

![メルアド_censored.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/44d2aea6-5281-5083-4165-65caecb04fe9.jpeg)

（メールアドレスはサービスアカウントの詳細画面から確認可能）

共有が終わったら下準備は完了です。

この辺、割と忘れがちな部分なので注意してください。kazama_tは共有設定せずにアレコレとハマって数時間無駄にしました（ ;  ; ）

## コードを実装

さて、いよいよコードの実装を行っていきます。

### Laravelプロジェクトを作成

誰でも同じ結果になるように、今回はLaravelプロジェクトを作成するところから始めます。

https://github.com/kazama1209/laravel-sample

↑サンプルを用意したので、各自クローンして使ってください。

#### セットアップ

Dockerでちゃちゃっと準備を済ませます。

##### コンテナを起動
```
$ docker-compose up -d
```

##### コンテナ内に入る
```
$ docker-compose exec php bash
```

##### プロジェクトを作成
```
$ composer create-project laravel/laravel
```

##### ブラウザを確認

![スクリーンショット 2020-12-15 1.52.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/a7dd5617-1f3f-2975-0ffb-1348bdf9751e.png)

「localhost」へアクセスしてLaravelの初期画面が表示されていれば成功。

### google-api-php-clientをインストール

https://github.com/googleapis/google-api-php-client

Google Drive APIを使用するために公式のライブラリを準備します。

```
$ cd laravel
```

先ほど作成したプロジェクト内に移動。

```
$ composer require google/apiclient:^2.0

...

  - Downloading phpseclib/phpseclib (2.0.29)
  - Downloading psr/cache (1.0.1)
  - Downloading firebase/php-jwt (v5.2.0)
  - Downloading google/auth (v1.14.3)
  - Downloading google/apiclient-services (v0.156)
  - Downloading google/apiclient (v2.8.3)
  - Installing phpseclib/phpseclib (2.0.29): Extracting archive
  - Installing psr/cache (1.0.1): Extracting archive
  - Installing firebase/php-jwt (v5.2.0): Extracting archive
  - Installing google/auth (v1.14.3): Extracting archive
  - Installing google/apiclient-services (v0.156): Extracting archive
  - Installing google/apiclient (v2.8.3): Extracting archive
4 package suggestions were added by new dependencies, use `composer suggest` to see details.
Package phpunit/php-token-stream is abandoned, you should avoid using it. No replacement was suggested.
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: fruitcake/laravel-cors
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
68 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
```

google-api-php-clientをインストール。

### Googleドライブにファイルをアップロードするための関数を定義

```
$ mkdir app/Libs
$ touch GoogleDrive.php
```

今回はapp/配下に「Libs」というフォルダを用意し、「GoogleDrive.php」ファイル内に関数を定義していきます。

```php:app/Libs/GoogleDrive.php
<?php

namespace App\Libs;

class GoogleDrive
{
    /**
     * Googleドライブへの認証を行う
     * @return Google_Service_Drive
     */
    public function getDriveClient(): \Google_Service_Drive
    {
        $client = new \Google_Client();

        // サービスアカウント作成時にダウンロードしたJSONファイルの名前を「client_secret」変更し、configフォルダ内に設置
        $client->setAuthConfig(config_path('client_secret.json'));
        $client->setScopes(['https://www.googleapis.com/auth/drive']);

        return new \Google_Service_Drive($client);
    }

    /**
     * ファイルをアップロードする
     *
     * @return GoogleDrive
     */
    public function fileUpload()
    {
        $driveClient = $this->getDriveClient();

        $fileMetadata = new \Google_Service_Drive_DriveFile([
            'name' => 'sample.jpg', // Googleドライブへアップロードされた際のファイル名（今回は「sample.jpg」とする）
            'parents' => ['xxxxxxxxxxxxxxxxxx'], // 保存先のフォルダID（配列で渡さなければならないので注意）
        ]);

        $driveClient->files->create($fileMetadata, [
            'data' => file_get_contents(storage_path('app/public/sample.jpg')), // アップロード対象となるファイルのパス（今回はstorage/app/public配下の「sample.jpg」を指定）
            'mimeType' => ' image/jpeg',
            'uploadType' => 'media',
            'fields' => 'id',
        ]);
    }
}
```

全体的にかなりシンプルな記述にしてあるので、細かい説明は割愛しても大丈夫だと思います。（各種コメントから何となく察して欲しい）

※アップロードに使用する画像は各自用意してapp/Storage/app/public配下に設置してください。

### Artisanコマンドを作成

動作確認しやすいようにArtisanコマンドを作成していきます。

```
$ php artisan make:command GoogleDriveFileUpload
```

ターミナルで↑を打ち込むとapp/Console/Commands/配下に「GoogleDriveFileUpload.php」というファイルが作成されているはずなので、次のように変更してください。

```php:app/Console/Commands/GoogleDriveFileUpload.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Libs\GoogleDrive;

class GoogleDriveFileUpload extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'google_drive:file_upload';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Upload file to Google Drive';

    /**
     * @var GoogleDrive
     */
    public $googleDrive;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(GoogleDrive $googleDrive)
    {
        parent::__construct();
        $this->googleDrive = $googleDrive;
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $this->googleDrive->fileUpload();
    }
}
```

### Artisanコマンドを実行

```
$ php artisan google_drive:file_upload
```

![アップロード成功.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/688854/ab088878-6474-8ef9-35d4-85870e1bad0a.png)

先ほど作成したArtisanコマンドを実行し、Googleドライブ内に「sample.jpg」というファイルがアップロードされていれば成功です。

## あとがき

お疲れ様でした。基本的な流れは以上になります。
あとは各自のプロジェクトに合わせてカスタマイズしてみてください。