How to Store Files on Google Cloud Storage Using PHP

July 25, 2023
Written by
Reviewed by
Paul Kamp
Twilion

How to Store Files on Google Cloud Storage Using PHP

There are numerous ways that you can store files using PHP. For example, you can store them on the local filesystem, regardless of the underlying filesystem technology, such as EXT4 and ZFS. You can store them on a NAS on your organisation's network. You can also use one of the many cloud-based file storage solutions.

To simplify the process, the tutorial will use Flysystem with the Google Cloud Storage adapter.  I love recommending Flysystem because it provides a unified interface for interacting with many types of filesystems.

In this, the first of a series of articles, you're going to learn how to store files using Google Cloud Storage and Flysystem. You'll learn how to retrieve and view a list of files, as well as how to upload, rename, and delete files.

Prerequisites

To follow along with this tutorial, you will need the following:

Create the project directory structure

First up, create the project's directory structure and change into it, by running the following commands.

mkdir -p google-drive/data
cd google-drive

Create a Google Cloud Storage Bucket

Before you can write any code, you have to create a bucket to store the files. To do that:

  1. Log in to the Google Cloud Console
  2. Expand the navigation menu (the three horizontal lines in the top left-hand corner of the Console)
  3. Navigate to Product and Solutions > All Products > Storage > Cloud Storage > Buckets

An empty list of buckets in the Google Cloud Storage Console

When there, either click CREATE at the top of the page or CREATE BUCKET in the centre of the page, near the bottom.

The Create a Bucket wizard in the Google Cloud Storage Console

Then, add a friendly name for the bucket, such as php-data-bucket. After that, click Choose how to control access to objects, and under Access control choose Fine-grained. This is required as Flysystem sets filesystem ACLs on all uploaded files. Finally, click CREATE.

Feel free to explore the other options in the Buckets documentation if you like. But the ones you've just set are all that you'll need for this tutorial.

The final modal dialog when creating a bucket  in the Google Cloud Storage Console, showing that public access will continue to be prevented.

Next, in the prompt that appears, leave Enforce public access prevention on this bucket enabled, and click CONFIRM. Without this, the objects in the bucket would be publicly accessible, which isn't what we want in this tutorial.

A newly created bucket, containing no objects in the Google Cloud Storage Console

You'll now be on the new bucket's page. From here, you can create new folders, upload files via drag and drop, and update the bucket's settings. Before moving on, upload a few files. That way, the code that retrieves all the files in the bucket, later, is more meaningful.

Create a Google Cloud Private Key

The page to create keys for a service account in the Google Cloud Storage Console

Now, you need to create a private key so that you can access the bucket programmatically with PHP. To do that, open the navigation menu again and navigate to Product and Solutions > All Products > Management > IAM and admin. Then, in the left-hand side navigation menu, click Service accounts. Click on your existing service account, then click KEYS > ADD KEY > Create new key.

The private key type popup window when creating keys for a service account  in the Google Cloud Storage Console

In the popup that appears, ensure that the key type is set to JSON and click CREATE. Then, move the JSON file that downloads to the project's data directory, in the top-level directory of the project, and name it google-storage.json.

Now, you're ready to write some PHP code!

Install the required dependencies

Start by installing the Google Cloud Storage adapter for Flysystem, the sole required dependency, by running the following command.

composer require league/flysystem-google-cloud-storage

List all files in a bucket

Next, create a new PHP file named google-cloud-storage.php in the project's top-level directory. In the new file, paste the code below. After you've done that, replace <<BUCKET NAME>> with the name you gave to your Google Cloud Bucket, earlier.

<?php

declare(strict_types=1);

use Google\Cloud\Storage\StorageClient;
use League\Flysystem\FileAttributes;
use League\Flysystem\FilesystemException;
use League\Flysystem\Filesystem;
use League\Flysystem\GoogleCloudStorage\GoogleCloudStorageAdapter;
use League\Flysystem\StorageAttributes;
use League\Flysystem\UnableToMoveFile;
use League\Flysystem\UnableToWriteFile;

include __DIR__.'/vendor/autoload.php';

$storageClient = new StorageClient([
    'keyFilePath' => __DIR__ . '/data/google-storage.json',
]);
$bucket = $storageClient->bucket('<<BUCKET NAME>>');
$adapter = new GoogleCloudStorageAdapter($bucket);
$filesystem = new Filesystem($adapter);

$files = $filesystem->listContents('/')
    ->filter(fn (StorageAttributes $attributes) => $attributes->isFile())
    ->sortByPath()
    ->toArray();

echo "Files available in {$bucket->name()}:\n";
/** @var FileAttributes $file */
foreach ($files as $file) {
    printf("- %s,%s,%s\n", $file->fileSize(), $file->mimeType(), $file->path());
}

The code starts by instantiating a new StorageClient object ($storageClient). These are available as part of the Cloud Storage Client for PHP package upon which the Google Cloud Storage adapter for Flysystem is based. This object provides the underlying connectivity with the Cloud Storage Bucket.

Then, it sets the bucket's name, and initialises a new GoogleCloudStorageAdapter object ($adapter) which it uses to initialise a Flysystem object ($filesystem).

It then attempts to retrieve a list of all objects in the bucket, filtering out anything that isn't a file, and sorts them alphabetically by their path name. Assuming that the operation was a success, for each file, its size, mime type, and path/name is printed to the terminal.

Test that the code works

Now that you've gotten this far, it's time to test that the code works, by running the command below.

php google-cloud-storage.php

You should see output, similar to the following, printed to the terminal.

Files available in php-data-bucket:
- 3475838,image/png,linux-commands-cheat-sheet.png

If Symfony is your preferred framework, take a look at the Flysystem Bundle. It lets you integrate Flysystem pretty tightly with any Symfony project.

Upload a file to a bucket

Now, it's time to upload a file to the bucket. To do that, add the following code to the end of google-cloud-storage.php, and replace <<A FILE YOU WANT TO UPLOAD>> with the local path to a file that you want to upload.

try {
    $filesystem->writeStream('/', fopen(__DIR__ . '/<<A FILE YOU WANT TO UPLOAD>>', 'r'));
    echo "File was successfully uploaded/written to Google Cloud Storage.";
} catch (FilesystemException | UnableToWriteFile $e) {
    echo "Could not upload/write the file to Google Storage. Reason: {$e->getMessage()}";
}

The code streams the file to the bucket using the writeStream() method, reducing the operation's required memory footprint. If the operation was successful, a confirmation is printed to the terminal, otherwise, the reason for the operation failing is printed.

Test that the changes work

Now, test that you can upload a file, by running the command below.

php google-cloud-storage.php

You should see "File was successfully uploaded/written to Google Cloud Storage." printed to the terminal.

Here are some things to bear in mind when working with Google Cloud Storage:

  • A bucket name is limited to 63 characters. However, if it contains a dot (.), the limit is extended to 222 characters.
  • A bucket object can be no larger than 5 TiB
  • Creation and deletion requests are limited to 1 request every 2 seconds

Check out the Quotas & limits documentation for further details.

Rename a file in a bucket

Next, it's time to add the ability to rename a file. To do that, in google-cloud-storage.php replace the upload file code with the following code. Then, replace <<A FILE YOU WANT TO RENAME>> with the name of the file in the bucket that you want to rename and <<NEW FILE NAME>> with the file's new name.

$source = '<<A FILE YOU WANT TO RENAME>>';
$destination = '<<NEW FILE NAME>>';
if (! $filesystem->fileExists($source)) {
    echo "Cannot move/rename {$source} as it does not exist.";
    exit();
}

try {
    $filesystem->move($source, $destination);
    echo "File was successfully moved from {$source} to {$destination}.";
} catch (FilesystemException | UnableToMoveFile $e) {
    echo "Could not move/rename the file: {$source}. Reason: {$e->getMessage()}";
}

The code starts by checking if the specified file exists in the bucket. If so, it uses the move() method to rename the file. If the operation was successful, a confirmation is printed to the terminal. Otherwise, the reason for the operation failing is printed.

Combining the ability to move and rename a file in one command is similar to how the Linux mv command and Microsoft Window's move command work.

Test that the changes work

 With the changes made, test that you can rename a file by running the command below.

php google-cloud-storage.php

You should see a confirmation message printed to the terminal, showing the original name (and path) of the file, along with the new one. If you go back to the bucket in your browser, you should see that the file has been renamed.

Are you a passionate Laravel user? Then you'll be glad to know that Flysystem ships with the default installation of Laravel—and it provides tight integration with the framework. Check out Laravel's File Storage documentation for further details.

Delete a file in a bucket

Now, for the final operation, deleting a file from the bucket. To do that, replace the rename code in google-cloud-storage.php with the following code. Then, in the new code, replace <<A FILE YOU WANT TO DELETE>> with the name of the file in the bucket that you want to delete.

$source = "<<A FILE YOU WANT TO DELETE>>";
if (! $filesystem->fileExists($source)) {
    echo "Cannot delete {$source} as it does not exist.";
    exit();
}

try {
    $filesystem->delete($source);
    echo "File ({$source}) was successfully deleted.";
} catch (FilesystemException | UnableToMoveFile $e) {
    echo "Could not delete the file: {$source}. Reason: {$e->getMessage()}";
}

Similar to the rename functionality, it checks if the file to delete ($source) exists. If so, it attempts to delete it. If the operation succeeded, then a confirmation message is printed to the terminal. Otherwise, the reason for the operation failing is printed.

Test that the code works

Let's test the final change in functionality, by running the command below.

php google-cloud-storage.php

You should see a confirmation message saying that the file was deleted. If you go back to the bucket in your browser, you should see that it is no longer there.

That's how to store files on Google Cloud Storage using PHP

Using PHP's Flysystem library with the Google Cloud Storage adapter makes storing files and other objects in Google Cloud Storage pretty easy—and, dare I say, enjoyable. It also lets you work with other storage services and filesystems with the same, unified interface. A definite time saver!

Have a look at the package's documentation, if you haven't already, and have a play with the code, which you can find on GitHub. I'd love to know what you think of Flysystem and the Google Cloud Storage adapter

Matthew Setter is a PHP Editor in the Twilio Voices team and a PHP and Go developer. He’s also the author of Mezzio Essentials and Deploy With Docker Compose. When he's not writing PHP or Go code, he's editing great PHP and Go articles here at Twilio. You can find him at msetter[at]twilio.com, on LinkedInTwitter, and GitHub.