google/cloud-storage
Idiomatic PHP client for Google Cloud Storage. Upload, download, and manage buckets/objects, set ACLs, and use the gs:// stream wrapper. Part of the Google Cloud PHP suite with full API docs and authentication guidance.
Installation:
composer require google/cloud-storage
Authentication (via .env or service account):
GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
Or configure in code:
$storage = new \Google\Cloud\Storage\StorageClient([
'keyFilePath' => storage_path('app/google-credentials.json'),
'projectId' => env('GOOGLE_CLOUD_PROJECT'),
]);
First Use Case: Upload a file from Laravel’s public disk:
use Google\Cloud\Storage\StorageClient;
$storage = new StorageClient();
$bucket = $storage->bucket(env('GOOGLE_CLOUD_BUCKET'));
$bucket->upload(
fopen(storage_path('app/uploads/example.pdf'), 'r'),
['name' => 'processed/example.pdf']
);
$storage->bucket('name')$bucket->object('path/to/file')$storage->registerStreamWrapper(); (enables gs:// URLs in file_get_contents)Use the Stream Wrapper to replace local/s3 disks in Laravel’s config/filesystems.php:
'disks' => [
'gcs' => [
'driver' => 'google',
'bucket' => env('GOOGLE_CLOUD_BUCKET'),
'project_id' => env('GOOGLE_CLOUD_PROJECT'),
'stream_wrapper' => true, // Enable gs:// URLs
],
],
Usage:
Storage::disk('gcs')->put('file.txt', 'Hello, GCS!');
$contents = file_get_contents('gs://my-bucket/file.txt');
Leverage multipart uploads for files >5MB:
$object = $bucket->upload(
fopen($localPath, 'r'),
[
'name' => 'large-file.zip',
'resumable' => true,
'chunkSize' => 5 * 1024 * 1024, // 5MB chunks
]
);
Generate time-limited URLs for user downloads:
$object = $bucket->object('private-file.pdf');
$url = $object->generateSignedUrl([
'expiration' => time() + 3600, // 1 hour
'responseDisposition' => 'attachment; filename="custom-name.pdf"',
]);
Configure bucket-level retention policies:
$bucket->update([
'retentionPeriod' => 365, // Days (immutable)
'softDelete' => true,
]);
Use Cloud Storage triggers (via Pub/Sub) to process uploads:
// In a Laravel job queue
$object = $bucket->object('uploaded/image.jpg');
$object->downloadToFile(sys_get_temp_dir() . '/temp.jpg');
// Process with Intervention Image, then save back to GCS
Attach metadata to objects (e.g., for Spatie Media Library):
$bucket->upload(
fopen($localPath, 'r'),
[
'name' => 'user-avatar.jpg',
'metadata' => [
'user_id' => auth()->id(),
'mime_type' => 'image/jpeg',
],
]
);
List and iterate over objects efficiently:
foreach ($bucket->objects() as $object) {
if ($object->name()->startsWith('logs/')) {
$object->delete();
}
}
Service Account Permissions:
roles/storage.admin (or least-privilege roles like roles/storage.objectAdmin).GOOGLE_APPLICATION_CREDENTIALS in .env for local testing, but avoid committing credentials.Deprecated Keys:
keyFile and keyFilePath are deprecated. Use environment variables or GOOGLE_APPLICATION_CREDENTIALS instead.Stream Handling:
$stream = fopen($localPath, 'r');
$bucket->upload($stream, ['name' => 'file.txt']);
fclose($stream); // Critical!
Stream Wrapper Caveats:
gs:// URLs do not support file_put_contents directly. Use $bucket->upload() instead.storage.objects.get for downloads.CRC32C Checksums:
$storage = new StorageClient(['checksum' => 'none']);
Enable Debug Logging:
$storage = new StorageClient([
'debug' => true,
'logPath' => storage_path('logs/gcs.log'),
]);
Common Errors:
InvalidArgumentException: Check bucket/object names (must be lowercase, no / in object names).Google\Cloud\Core\Exception\GoogleException: Inspect the getMessage() for API-specific errors (e.g., quota limits).Retry Behavior:
$storage = new StorageClient([
'retry' => [
'maxAttempts' => 5,
'timeout' => 30,
],
]);
Custom Metadata Handling:
Google\Cloud\Storage\Object class to add Laravel-specific metadata:
$object->setLaravelMetadata(['user_id' => auth()->id()]);
Event Listeners:
Storage facade to hook into uploads:
Storage::disk('gcs')->addListener('afterWrite', function ($event) {
// Trigger a job to process the uploaded file
});
Hybrid Storage:
FilesystemManager to fallback to local storage:
$disk = Storage::disk('gcs');
if (!$disk->exists('file.txt')) {
Storage::disk('local')->copy('backup/file.txt', 'gcs:file.txt');
}
Predefined ACLs:
publicRead in production. Use signed URLs or IAM conditions instead.$object->update([
'acl' => [
[
'entity' => 'user-' . auth()->id(),
'role' => 'READER',
],
],
]);
Object Versioning:
$bucket->update(['versioning' => true]);
CORS Configuration:
$bucket->update([
'cors' => [
[
'origin' => ['https://your-app.com'],
'method' => ['GET', 'HEAD', 'PUT'],
'responseHeader' => ['Content-Type'],
],
],
]);
How can I help you explore Laravel packages today?