Upload Files to Google Cloud Storage (GCS) From the Browser

Simen Andresen
Feb 7, 2018 · 2 min read

After having spent the better part of a day struggling to upload files straight from the browser to GCS I felt the internet could do good with a how-to describing the result.

Background

We use GCS to store files for our app.imerso.com platform. However, we have implemented our own authentication and don’t require our users to be connected to google. Therefore, we cannot rely on standard Google APIs Client Library for JavaScript for authorization, instead we use Signed URLs as the weapon of choice.

Solution

So, we wanted to authorize the upload from our back-end, but let the transfer of files go directly from the frontend to GCS’s API, making sure only our authenticated users can upload files to our GCS buckets. We therefore make user-authenticated requests to our REST API, the REST API signs a URL for making a PUT request with the specified Filename and Content-Type and returns the signed URL to the front-end:

High level overview of the upload flow

Backend

Our back-end is written in Scala, and we use the following code to create a signed URL that can be used by the front-end to upload files:

Make sure to create a service account key for authenticating the back-end to your GCP project. Once you have created a service account json key, you need to set an environment variable to point to the key on the server: GOOGLE_APPLICATION_CREDENTIALS="path/to/service-account/key.json , otherwise storage.create will throw a StorageException . Also, make sure that the service account used here has the right permissions on the bucket you use, e.g. “Object Creator” or “Object Admin”.

Frontend

GCS offers two different API’s for GCS:

  • XML-API
  • JSON-API

Unfortunately (at the time of writing) only the XML-API supports signed URL’s so that’s what we’re stuck with.

Here’s an example of how to upload a file using the XML-API using the signedUrl provided from our REST-API:

To run this in a browser, make sure that the bucket (specified in the backend code) has the correct CORS settings (Check the current CORS settings with gsutil: gsutil cors get gs://<your bucket> ). The following should work:

 [{“maxAgeSeconds”: 3600, “method”: [“GET”, “HEAD”, “PUT”], “origin”: [<http://your-super-awesome-domainz.gov>], “responseHeader”: [“Content-Type”, “Access-Control-Allow-Origin”]}]`

That’s it, I hope someone finds this useful!

ImersoTechBlog

Software and engineering at Imerso (We are hiring! see https://startupmatcher.com/s/imerso)

Simen Andresen

Written by

Co-founder, CTO and full-stack developer at Imerso. Working with 3D scanning and quality control solutions for the construction industry.

ImersoTechBlog

Software and engineering at Imerso (We are hiring! see https://startupmatcher.com/s/imerso)