This is part of the writeup on Using Google Cloud Storage with Apps Script. By now you should have completed all the steps in Setting up or creating a console project, Enabling APIs and OAuth2 and Using the service account to enable access to cloud storage and now have a project and credentials you can use to create your storage buckets. You can also read more about the background of how all this works at GcsStore overview – Google Cloud Storage and Apps Script
Libraries
You should already have the cGoa library, from when you set up your credentials in Using the service account to enable access to cloud storage
You’ll also need the cGcsStore library on GitHub or at this key.
1w0dgijlIMA_o5p63ajzcaa_LJeUMYnrrSgfOzLKHesKZJqDCzw36qorl
It’s not necessary, but I usually also include the cUseful library too, as it has lots of shortcuts and I may use some of them in the examples throughout this post. You’ll find that on Github or at
1EbLSESpiGkI3PYmJqWh3-rmLkYKAtCNPi1L2YCtMgo2Ut8xMThfJ41Ex
So my final resource manifest looks like this.
Some examples
These examples are various scenarios of writing and reading different kinds of data, exercising expiry, folderkey, compression and various other capabilities.
Getting started
Consuming an access token, opening the store, creating a bucket if necessary, setting up lifetime (I’m using this one as self-cleansing since I want it to go away later)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// ive named the goa service account package name and bucket name the same thing var bucket = 'xliberation-store'; var packageName = 'xliberation-store'; // this consumes up the Oauth2 authentication that was set up once off var goa = cGoa.make (packageName,PropertiesService.getScriptProperties()); // you can get the project id & accessToken from goa var accessToken = goa.getToken(); var projectId = goa.getPackage().project_id; // create a new gcs manager var gcs = new cGcsStore.GcsStore() .setAccessToken (accessToken) // a bucket can be created like this if (!gcs.bucketExists(bucket)) { gcs.createBucket (projectId, bucket); } // open a store with global visibility gcs.setBucket(bucket); // since im using it as gcs i'll set the lifetime of items in it to 1 day gcs.setLifetime (1); |
Putting and Getting data
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
// do a put & get -- global scope gcs.put ("mykey","written to global scope"); Logger.log(gcs.get ("mykey")); // an arbitrary scope gcs.setFolderKey ('all my friends'); gcs.put ("mykey","written to " + gcs.getFolderKey() + ' scope'); Logger.log(gcs.get ("mykey")); // lets use this function to check that what was got is what was written // from now on function checkit(key,value,expiry) { gcs.put (key,value,expiry); var result = gcs.get(key); if (result !== value) { throw 'expected:' + value + "\ngot:" + result; } } // do a put & get -- script scope gcs.setFolderKey ('scripts/' + ScriptApp.getScriptId()); checkit ("mykey","written to " + gcs.getFolderKey() + ' scope'); // a document scope gcs.setFolderKey ('documents/some document id'); checkit ("mykey","written to " + gcs.getFolderKey() + ' scope'); // users - use whatever syntax you like. I recommend an object name like this gcs.setFolderKey ('users/bruce@mcpher.com'); checkit ("mykey","written to " + gcs.getFolderKey() + ' scope'); // script users gcs.setFolderKey ('scripts/' + ScriptApp.getScriptId() + '/users/bruce@mcpher.com'); checkit("mykey","written to " + gcs.getFolderKey() + ' scope'); // anonymous users can be tracked using the UserProperties service var registration = cUseful.UserRegistration.register (PropertiesService.getUserProperties(), 'xliberation_registration_key'); gcs.setFolderKey ('anonymous/' + registration.id); checkit ("mykey","written to " + gcs.getFolderKey() + ' scope - number of visits=' + registration.visits); //objects can be written as well as strings - they are stringified when written and parsed on return //update the registration object registration.update(); gcs.put ("anobject",registration); var result = gcs.get ("anobject"); if (JSON.stringify(result) !== JSON.stringify(registration)) { throw 'expected-' + JSON.stringify(registration) + "-got-" + JSON.stringify(result); } // since the visibilitykey is arbitrary you can use anything you like // so you could create a document/user with gcs.setFolderKey ('documents/some document id/anonymous/' + registration.id); checkit ("mykey","written to " + gcs.getFolderKey() + ' scope - number of visits=' + registration.visits); |
Getting the keys in the store
1 2 |
// here's how to get all the unexpired keys for a particular visibility Logger.log(gcs.setFolderKey("anonymous").getKeys()); |
Expiration
1 2 3 4 5 6 7 8 9 10 11 12 |
// expiration works like this // it should find it as it hasnt expired yet checkit("expireme","this should be expired in 2 seconds", 2); // wait 3 seconds, and it should have gone away Utilities.sleep (3000); if (gcs.get ("expireme")) { throw "should have expired"; } else { Logger.log('expired ok'); } |
Working with blobs
GcsStore can write blobs too.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
// writing a blob - lets put them somewhere easy to find gcs.setFolderKey("myblobs"); var blob = UrlFetchApp.fetch( 'https://lh3.googleusercontent.com/WDw-R5uUz7VnjhPAAyoDYXPWrLKATvUeKeeQk1HA1AFO-tqyVjwmAAKHWDZcJEISO8kRwtV7nQ=s50-h50-e365' ).getBlob(); gcs.put ("ablob",blob); var getBlob = gcs.get("ablob"); if (!blobMatches (blob, getBlob)); // use this to compare blobs function blobMatches(a,b) { var abytes = a.getBytes(); var bbytes = b.getBytes(); if (abytes.length !== bbytes.length) { throw 'expected length of ' + abytes.length + ' but got ' + bbytes.length; } var acontent = a.getContentType(); var bcontent = b.getContentType(); if (acontent !== bcontent) { throw 'expected content of ' + acontent + ' but got ' + bcontent; } if (!abytes.every(function(d,i) { return d === bbytes[i]; })) { throw 'bytes are different'; } } |
Default expiration
By default items don’t expire, but you can make them
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// now lets change the default expiry time var current = gcs.getDefaultExpiry (); gcs.setFolderKey("global") .setDefaultExpiry (3); checkit("expireme1","this should be expired in 3 seconds"); // set it back to whatever it was gcs.setDefaultExpiry(current); Utilities.sleep (3000); if (gcs.get ("expireme1")) { throw "should have expired"; } else { Logger.log('expired ok'); } |
Compressing
GcsStore can compress and uncompress the data on writing/reading if you want it to.
1 2 3 4 5 6 7 8 9 |
// we can ask for the data to be compressed for a single call - heres compress with a 10 second expiry gcs.put ("compressed" , "this should be compressed", 10 , true); Logger.log(gcs.get("compressed")); // we can also set that as the default var current = gcs.getDefaultCompress(); gcs.setDefaultCompress(true); checkit("compress1","this should be compressed by default"); gcs.setDefaultCompress(current); |
Predefined ACLS
You can change the predefined ACL of an existing object as in the example below which sets its access to public read.
gcs.patchPredefinedAcl(“compress1”, “publicRead”);
SelfLink
If you want to share an object, you can get its self-link like this, which will return a URL to access its metadata directly. If you’ve set the ACL to publicRead, this would allow you share a file’s metadata publicly. To get the link to its content, use getPublicLink
Logger.log(gcs.getSelfLink(“compress1”));
PublicLink
If you want to share an object, you can get its self-link like this, which will return a URL to access its content directly. If you’ve set the ACL to publicRead, this would allow you share a file publicly.
Logger.log(gcs.getPublicLink(“compress1”));
CORS
GcsStore has methods to enable cors. For more info see Google cloud storage and CORS
More
For more detail on all this, see GcsStore overview – Google Cloud Storage and Apps Script