LoginSignup
1

More than 1 year has passed since last update.

Laravelのカスタムファイルシステムを使用してCloudStorageを統合する

Last updated at Posted at 2021-04-10

Laravelのファイルストレージを利用してCloudStorageを扱えるようにしてみます。Laravelに統合するにあたってゼロからつくるのではなくすでにパッケージが有志の方によって作られているのでこれを利用したいと思います。

導入

READMEに書かれている通りcomposeをつかってインストールします。インストールした後は、.envとfilesystem.phpに必要な情報を追加してください。

エラーを回避する

バケットのアクセスコントロールが「均一」になっているとlaravel-google-cloud-storageを使ってオブジェクトをアップロードすると失敗します。

Cannot insert legacy ACL for an object when uniform bucket-level access is enabled

おそらく原因はgithubのissueのコメントに書かれていることなのではないかと思います。
https://github.com/Superbalist/laravel-google-cloud-storage/issues/80#issuecomment-616557477

なので、このままではバケットのアクセスコントロールが「均一」になっているとこのファイルシステムでのアプロード機能が使えなくなるので、少し手を加えます。

ServiceProviderを作成する

エラーが起きる原因は、laravel-google-cloud-storageがアダプターとしてflysystem-google-cloud-storageを利用していることにあります。エラーを回避する方法がプルリクエストで提案されています。要は、GoogleStorageAdapterクラスのgetOptionsFromConfigメソッドをいじりたいので、GoogleStorageAdapterクラスを拡張したクラスを作成して、getOptionsFromConfigメソッドをオーバーライドしてアクセスコントロールが「均一」のバケットにも対応できるように処理を修正して、このクラス自身を返すというものです。これをLaravelのカスタムファイルシステムで実現させるために、独自のサービスクラスを作ります。作成したサービスプロバイダーの中身は下記になります。

app/providers/GoogleCloudStorageServiceProvider.php
<?php

namespace App\Providers;

use Google\Cloud\Storage\StorageClient;
use Illuminate\Support\Arr;
use Superbalist\LaravelGoogleCloudStorage\GoogleCloudStorageServiceProvider as GCSProvider;
use Superbalist\Flysystem\GoogleStorage\GoogleStorageAdapter;

class GoogleCloudStorageServiceProvider extends GCSProvider
{
    /**
     * Create a new StorageClient
     *
     * @param  mixed $config
     * @return \Google\Cloud\Storage\StorageClient
     */
    private function createClient($config)
    {
        $keyFile = Arr::get($config, 'key_file');
        if (is_string($keyFile)) {
            return new StorageClient([
                'projectId' => $config['project_id'],
                'keyFilePath' => $keyFile,
            ]);
        }

        if (! is_array($keyFile)) {
            $keyFile = [];
        }
        return new StorageClient([
            'projectId' => $config['project_id'],
            'keyFile' => array_merge(["project_id" => $config['project_id']], $keyFile)
        ]);
    }

    public function boot()
    {
        $factory = $this->app->make('filesystem');
        /* @var FilesystemManager $factory */
        $factory->extend('gcs', function ($app, $config) {
            $storageClient = $this->createClient($config);

            $bucket = $storageClient->bucket($config['bucket']);
            $pathPrefix = Arr::get($config, 'path_prefix');
            $storageApiUri = Arr::get($config, 'storage_api_uri');

            $adapter = $this->resolveAdapter($storageClient, $bucket, $pathPrefix, $storageApiUri);

            return $this->createFilesystem($adapter, $config);
        });
    }

    public function resolveAdapter($storageClient, $bucket, $pathPrefix = null, $storageApiUri = null)
    {
        return new class ($storageClient, $bucket, $pathPrefix, $storageApiUri) extends GoogleStorageAdapter {
            protected function getOptionsFromConfig(\League\Flysystem\Config $config)
            {
                $options = [];

                if (empty($this->bucket->info()['iamConfiguration']['uniformBucketLevelAccess']['enabled'])) {
                    if ($visibility = $config->get('visibility')) {
                        $options['predefinedAcl'] = $this->getPredefinedAclForVisibility($visibility);
                    } else {
                        $options['predefinedAcl'] = $this->getPredefinedAclForVisibility(AdapterInterface::VISIBILITY_PRIVATE);
                    }
                }

                if ($metadata = $config->get('metadata')) {
                    $options['metadata'] = $metadata;
                }

                return $options;
            }
        };
    }
}

createFilesystemに渡す$adapterに、GoogleStorageAdapterを拡張したクラスを渡しています。またこのサービスクラスもlaravel-google-cloud-storageのGoogleCloudStorageServiceProviderを拡張しています。
作成したサービスプロバイダーをapp.phpに登録します。

config/app.php
...
'providers' => [
    ...
    App\Providers\GoogleCloudStorageServiceProvider::class,
],
...

必要な作業は終わりました。これでアクセスコントロールが「均一」のバケットに対してもオブジェクトがアップロードできるようになります。

    public function upload(Request $request)
    {
        $file = $request->file('test');
        $disk = Storage::disk('gcs');
        $disk->put('test.txt', file_get_contents($file));
    }

Laravelでカスタムファイルシステムを作成する方法については、下記の記事で解説しているのでぜひ参考にしてみてください。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1