Connect Google App Engine with Google Cloud Storage Php by Rajesh Kumar Sahanee - June 6, 2020June 6, 20200 Post Views: 5,632 Hello Friends, I hope you are doing well. Today we are going to see how to connect Google App Engine with Google Cloud Storage. App Engine is a fully managed, serverless platform for developing and hosting web applications at scale while Cloud Storage is a RESTful file storage service for storing and accessing data on Google Cloud Platform. Google Cloud Storage service can be used to serve website content, store data for archival and disaster recovery or distributing large files to users via direct download. Here we are going to develop a one page app which will give us options to upload file, list files as well as delete files. So, without wasting any time let’s do it by following below steps:- Step 1: Login to Google Cloud Console and Create a Project Step 2: Go to App Engine and Create Application using below command in Cloud Shell or you can also follow steps by clicking on create application see screenshots below for your reference Cloud Shell Shell gcloud app create --region=asia-south1 1 gcloud app create --region=asia-south1 OR Step 1 Step 2 Step 3 Step 4 Step 3: After receiving success open Cloud Shell if not already opened and click on pencil icon to Launch Editor Step 4: Download Google Cloud Storage Library using below command Cloud Shell Shell composer require google/cloud-storage 1 composer require google/cloud-storage Composer comes pre-installed on Cloud Shell so you need to install it, but still if you need guidance to install composer you check here https://getcomposer.org/. After executing above command you’ll see vendor directory which contains cloud storage library and two more files called composer.json and composer.lock Step 5: Now It’s Time to Code app.yaml app.yaml YAML # Use the PHP 7.3 runtime (BETA) by replacing "php72" below with "php73" runtime: php73 env_variables: BUCKET_NAME: "mydemoproject-278919.appspot.com" 12345 # Use the PHP 7.3 runtime (BETA) by replacing "php72" below with "php73"runtime: php73 env_variables: BUCKET_NAME: "mydemoproject-278919.appspot.com" We configure our App Engine app’s settings in the app.yaml file. The app.yaml file also contains information about your app’s code, PHP runtime, and entrypoint. Here we are defining an environment variable named as BUCKET_NAME which we will use in our code, this bucket name you can get from Navigation Menu => Storage Option in GCP Console. Actually, when we create App Engine project a bucket is automatically created by App Engine for the use in your app with named as “yourprojectid.appspot.com”. config.php config.php PHP <?php // load GCS library require __DIR__ . '/vendor/autoload.php'; use Google\Cloud\Storage\StorageClient; function uploadFile($bucketName, $fileContent, $cloudPath, $makePublic = false) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); // upload/replace file $object = $bucket->upload( $fileContent, ['name' => $cloudPath] // if $cloudPath is existed then will be overwrite without confirmation // NOTE: // a. do not put prefix '/', '/' is a separate folder name !! ); // is it succeed ? if($object != null) { if($makePublic) { $object->update(['acl' => []], ['predefinedAcl' => 'PUBLICREAD']); } return true; } return false; } function getFileInfo($bucketName, $cloudPath) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); $object = $bucket->object($cloudPath); return $object->info(); } function getFiles($bucketName, $directory = null) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); if ($directory == null) { // list all files $objects = $bucket->objects(); } else { // list all files within a directory (sub-directory) $options = array('prefix' => $directory); $objects = $bucket->objects($options); } return $objects; } function deleteFile($bucketName, $objectName, $options = []) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); try { $object = $bucket->object($objectName); $object->delete(); } catch (Exception $exc) { //exception handling } } 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 <?php // load GCS libraryrequire __DIR__ . '/vendor/autoload.php'; use Google\Cloud\Storage\StorageClient; function uploadFile($bucketName, $fileContent, $cloudPath, $makePublic = false) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); // upload/replace file $object = $bucket->upload( $fileContent, ['name' => $cloudPath] // if $cloudPath is existed then will be overwrite without confirmation // NOTE: // a. do not put prefix '/', '/' is a separate folder name !! ); // is it succeed ? if($object != null) { if($makePublic) { $object->update(['acl' => []], ['predefinedAcl' => 'PUBLICREAD']); } return true; } return false;} function getFileInfo($bucketName, $cloudPath) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); $object = $bucket->object($cloudPath); return $object->info();} function getFiles($bucketName, $directory = null) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); if ($directory == null) { // list all files $objects = $bucket->objects(); } else { // list all files within a directory (sub-directory) $options = array('prefix' => $directory); $objects = $bucket->objects($options); } return $objects;} function deleteFile($bucketName, $objectName, $options = []) { $storage = new StorageClient(); // set which bucket to work in $bucket = $storage->bucket($bucketName); try { $object = $bucket->object($objectName); $object->delete(); } catch (Exception $exc) { //exception handling }} All necessary functions for uploading, listing and deleting files are defined here in this file index.php index.php PHP <?php # [START gae_simple_front_controller] switch (@parse_url($_SERVER['REQUEST_URI'])['path']) { case '/': require 'home.php'; break; case '/requests.php': require 'requests.php'; break; default: http_response_code(404); exit('Not Found'); } ?> 1234567891011121314 <?php# [START gae_simple_front_controller]switch (@parse_url($_SERVER['REQUEST_URI'])['path']) { case '/': require 'home.php'; break; case '/requests.php': require 'requests.php'; break; default: http_response_code(404); exit('Not Found');}?> User requests routing code which responds according to path i.e opening home page sending ajax request to requests.php showing 404 on invalid path home.php home.php PHP <?php require_once 'config.php'; if (!getenv('BUCKET_NAME')) { die('Set BUCKET_NAME environment variables'); } $bucketName = getenv('BUCKET_NAME'); if (isset($_REQUEST['delete']) && isset($_REQUEST['name'])) { $fileName = urldecode($_REQUEST['name']); deleteFile($bucketName, $fileName); //header("Location: /"); echo '<script>window.location.href = "/"</script>'; exit(); } $objects = getFiles($bucketName); ?> <html> <head> <title>Objects in Bucket</title> </head> <body> <h1>Objects in Bucket</h1> <form id="fileUploadForm" method="post" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit" name="upload" value="Upload"/> <span id="uploadingmsg"></span> <hr/> </form> <table border="1" style="width: 100%;"> <thead> <tr> <td>Object</td> <td>Action</td> </tr> </thead> <tbody id="objects"> <?php foreach ($objects as $o) { $oinfo = $o->info(); ?> <tr> <td><a href="https://storage.googleapis.com/<?= $oinfo['bucket'] ?>/<?= $oinfo['name'] ?>"><?= $o->name() ?></a></td> <td><a href="/?delete=1&name=<?= urlencode($o->name()) ?>">Delete</a></td> </tr> <?php } ?> </tbody> </table> <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script> <script> $("#fileUploadForm").submit(function (e) { e.preventDefault(); var action = "requests.php?action=upload"; $("#uploadingmsg").html("Uploading..."); var data = new FormData(e.target); $.ajax({ type: 'POST', url: action, data: data, /*THIS MUST BE DONE FOR FILE UPLOADING*/ contentType: false, processData: false, }).done(function (response) { $("#uploadingmsg").html(response.msg); //https://storage.googleapis.com/[BUCKET_NAME]/[OBJECT_NAME] if (response.code == '200') { window.location.reload(); } }).fail(function (data) { //any message }); }); </script> </body> </html> 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576 <?phprequire_once 'config.php'; if (!getenv('BUCKET_NAME')) { die('Set BUCKET_NAME environment variables');} $bucketName = getenv('BUCKET_NAME');if (isset($_REQUEST['delete']) && isset($_REQUEST['name'])) { $fileName = urldecode($_REQUEST['name']); deleteFile($bucketName, $fileName); //header("Location: /"); echo '<script>window.location.href = "/"</script>'; exit();} $objects = getFiles($bucketName);?><html> <head> <title>Objects in Bucket</title> </head> <body> <h1>Objects in Bucket</h1> <form id="fileUploadForm" method="post" enctype="multipart/form-data"> <input type="file" name="file"/> <input type="submit" name="upload" value="Upload"/> <span id="uploadingmsg"></span> <hr/> </form> <table border="1" style="width: 100%;"> <thead> <tr> <td>Object</td> <td>Action</td> </tr> </thead> <tbody id="objects"> <?php foreach ($objects as $o) { $oinfo = $o->info(); ?> <tr> <td><a href="https://storage.googleapis.com/<?= $oinfo['bucket'] ?>/<?= $oinfo['name'] ?>"><?= $o->name() ?></a></td> <td><a href="/?delete=1&name=<?= urlencode($o->name()) ?>">Delete</a></td> </tr> <?php } ?> </tbody> </table> <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script> <script> $("#fileUploadForm").submit(function (e) { e.preventDefault(); var action = "requests.php?action=upload"; $("#uploadingmsg").html("Uploading..."); var data = new FormData(e.target); $.ajax({ type: 'POST', url: action, data: data, /*THIS MUST BE DONE FOR FILE UPLOADING*/ contentType: false, processData: false, }).done(function (response) { $("#uploadingmsg").html(response.msg); //https://storage.googleapis.com/[BUCKET_NAME]/[OBJECT_NAME] if (response.code == '200') { window.location.reload(); } }).fail(function (data) { //any message }); }); </script> </body></html> Code to list objects of given bucket, uploading and deleting objects (files) requests.php requests.php PHP <?php include_once 'config.php'; $action = filter_var(trim($_REQUEST['action']), FILTER_SANITIZE_STRING); if ($action == 'upload') { $response['code'] = "200"; if ($_FILES['file']['error'] != 4) { //set which bucket to work in $bucketName = getenv('BUCKET_NAME'); // get local file for upload testing $fileContent = file_get_contents($_FILES["file"]["tmp_name"]); // NOTE: if 'folder' or 'tree' is not exist then it will be automatically created ! $cloudPath = $_FILES["file"]["name"]; $isSucceed = uploadFile($bucketName, $fileContent, $cloudPath, true); if ($isSucceed == true) { $response['msg'] = 'SUCCESS: to upload ' . $cloudPath . PHP_EOL; // TEST: get object detail (filesize, contentType, updated [date], etc.) $response['data'] = getFileInfo($bucketName, $cloudPath); } else { $response['code'] = "201"; $response['msg'] = 'FAILED: to upload ' . $cloudPath . PHP_EOL; } } header("Content-Type:application/json"); echo json_encode($response); exit(); } 123456789101112131415161718192021222324252627282930 <?php include_once 'config.php'; $action = filter_var(trim($_REQUEST['action']), FILTER_SANITIZE_STRING);if ($action == 'upload') { $response['code'] = "200"; if ($_FILES['file']['error'] != 4) { //set which bucket to work in $bucketName = getenv('BUCKET_NAME'); // get local file for upload testing $fileContent = file_get_contents($_FILES["file"]["tmp_name"]); // NOTE: if 'folder' or 'tree' is not exist then it will be automatically created ! $cloudPath = $_FILES["file"]["name"]; $isSucceed = uploadFile($bucketName, $fileContent, $cloudPath, true); if ($isSucceed == true) { $response['msg'] = 'SUCCESS: to upload ' . $cloudPath . PHP_EOL; // TEST: get object detail (filesize, contentType, updated [date], etc.) $response['data'] = getFileInfo($bucketName, $cloudPath); } else { $response['code'] = "201"; $response['msg'] = 'FAILED: to upload ' . $cloudPath . PHP_EOL; } } header("Content-Type:application/json"); echo json_encode($response); exit();} This file is called through ajax to upload file on google cloud storage and here you can see $cloudPath variable which is used to store file path and if we need to store it in database we can do so and later we can fetch it from our database instead of cloud storage api. We can also use Cloud SQL in our application to make our application fully cloud based if you are not aware how to use Cloud SQL with App Engine then checkout this post Connect Google App Engine with Google Cloud SQL. Step 6: Go to Editor which we had launched in step 3 and upload all the files created in step 5. Step 7: Deploy App using below command Cloud Shell Shell gcloud app deploy OR gcloud app deploy --version v1 123 gcloud app deployORgcloud app deploy --version v1 Step 8: Access App Note: – We don’t have to create service account and download private key just as we had done in Upload File to Google Cloud Storage using PHP article as this code is running on App Engine and App Engine app by default connected to a service account which have access to Cloud Storage. Video https://zatackcoder.com/wp-content/uploads/2020/06/app-engine-with-cloud-storage.mp4 NetBeans Project Download App Engine with Cloud Storage 1 file(s) 930.80 KB Download References:- https://cloud.google.com/appengine/docs/standard/php7/config/appref https://cloud.google.com/appengine/docs/standard/php7/using-cloud-storage https://cloud.google.com/storage/docs/uploading-objects#storage-upload-object-code-sample https://cloud.google.com/storage/docs/access-control/making-data-public#storage-make-object-public-php https://cloud.google.com/storage/docs/listing-objects#storage-list-objects-php https://cloud.google.com/storage/docs/deleting-objects#storage-delete-object-php Thank you Friends Please don’t forget share if you it useful