league/flysystem-google-cloud-storage
Flysystem adapter for Google Cloud Storage (GCS). Install via composer to use GCS as a filesystem in Flysystem, with full support through the main Flysystem project. See official docs for configuration and usage.
Installation
composer require league/flysystem-google-cloud-storage
Configure Google Cloud SDK Ensure you have:
Storage Admin or appropriate permissions.service-account-key.json) downloaded from Google Cloud Console.gcloud auth application-default login for local testing).Basic Usage
use League\Flysystem\Filesystem;
use League\Flysystem\GoogleCloudStorage\GoogleCloudStorageAdapter;
$adapter = new GoogleCloudStorageAdapter('your-bucket-name', [
'keyFilePath' => '/path/to/service-account-key.json',
'projectId' => 'your-project-id',
]);
$filesystem = new Filesystem($adapter);
// Write a file
$filesystem->write('test.txt', 'Hello, GCS!');
// Read a file
$content = $filesystem->read('test.txt');
First Use Case
Replace local file storage in Laravel’s filesystem.php config:
'disks' => [
'gcs' => [
'driver' => 'google',
'bucket' => env('GCS_BUCKET'),
'keyFile' => env('GCS_KEY_PATH'),
'projectId' => env('GCS_PROJECT_ID'),
],
],
Then use via the facade:
Storage::disk('gcs')->put('file.jpg', file_get_contents('local.jpg'));
File Uploads with Metadata Leverage GCS’s metadata support for custom attributes:
$adapter->write('file.jpg', file_get_contents('local.jpg'), [
'metadata' => [
'customKey' => 'customValue',
'contentType' => 'image/jpeg',
],
]);
Signed URLs for Secure Downloads Generate time-limited URLs for client-side downloads:
$url = $adapter->getClient()->storage()->object('bucket', 'file.jpg')->signedUrl(
new \DateTime('+1 hour'),
['response-disposition' => 'attachment; filename="file.jpg"']
);
Streaming Large Files Use streams to avoid memory issues with large files:
$stream = fopen('local-large-file.zip', 'r');
$filesystem->writeStream('remote-large-file.zip', $stream);
fclose($stream);
Lifecycle Management Automate object expiration or transitions:
$adapter->getClient()->storage()->bucket('bucket')->update(
['lifecycle' => [
'rule' => [
'action' => ['type' => 'Delete'],
'condition' => ['age' => 30], // Delete after 30 days
],
]]
);
Laravel Integration: Publish the config and create a custom disk driver:
// app/Providers/AppServiceProvider.php
public function register()
{
Storage::extend('google', function ($app, $config) {
$client = new \Google_Client();
$client->setAuthConfig($config['keyFile']);
$client->addScope(\Google_Service_Storage::STORAGE);
$service = new \Google_Service_Storage($client);
return new Filesystem(new GoogleCloudStorageAdapter(
$config['bucket'],
['client' => $service]
));
});
}
Flysystem Events:
Listen to file system events (e.g., preWrite, postDelete) for logging/auditing:
$filesystem->addListener('preWrite', function ($event) {
logger()->info('Uploading file', ['path' => $event->path()]);
});
Caching: Use Flysystem’s caching layer to reduce API calls:
$adapter = new GoogleCloudStorageAdapter('bucket', [
'client' => $client,
'cache' => new \League\Flysystem\Cached\Storage\CachedAdapter(
$adapter,
new \League\Flysystem\Cached\Storage\MemoryCache()
),
]);
Permissions:
compute-engine service account for production (scope too broad).Bucket Naming:
Costs:
GET, PUT).CORS:
$adapter->getClient()->storage()->bucket('bucket')->update(
['cors' => [
['origin' => ['*'], 'method' => ['GET', 'HEAD'], 'responseHeader' => ['*']],
]]
);
Timeouts:
max_execution_time. Use streaming or chunked uploads.Enable Debugging:
$client = new \Google_Client();
$client->setDeveloperKey('your-api-key');
$client->setApplicationName('MyApp/1.0');
$client->setAuthConfig($keyFilePath);
$client->setUseBatch(true); // For batch operations
Logging: Enable GCP logging for API calls:
$client->setAccessType('offline');
$client->setScopes([\Google_Service_Storage::STORAGE]);
$client->setHandleAsSync(true); // For synchronous requests
Common Errors:
InvalidArgument: Check bucket name format or permissions.Google_Auth_Exception: Verify keyFilePath and scopes.Google_Service_Exception: Ensure the bucket exists and the object path is correct.Custom Metadata Handling: Extend the adapter to add custom metadata validation:
class CustomGoogleCloudStorageAdapter extends GoogleCloudStorageAdapter {
public function write($path, $contents, array $config = []) {
$config['metadata']['customField'] = 'defaultValue';
return parent::write($path, $contents, $config);
}
}
Event Dispatching: Integrate with Laravel Events or custom listeners:
$filesystem->addListener('postUpload', function ($event) {
event(new FileUploaded($event->path(), $event->contents()));
});
Fallback Storage: Implement a fallback to local storage if GCS is unavailable:
$adapter = new FallbackAdapter(
new GoogleCloudStorageAdapter('primary-bucket', [...]),
new LocalAdapter(new \League\Flysystem\Local\LocalFilesystem())
);
Async Operations: Use queues for background uploads/downloads:
Queue::push(UploadToGCS::class, [
'path' => 'file.jpg',
'contents' => file_get_contents('local.jpg'),
'disk' => 'gcs',
]);
How can I help you explore Laravel packages today?