Uploading photos to Google Cloud Storage through Firebase Callable Functions

Nick Felker
4 min readMar 8, 2021

The opinions stated here are my own, not those of my company.

Good morning!

A few weeks ago I was working on a side-project that involved checking into various entities. While this implementation is coffee, it could be easily swapped out for other types of crafts, like pizza or tea by changing a few strings. It’s also a Progressive Web App, making it easily accessible across platforms.

s/coffee/anything else/

Everything is hosted on Firebase, using its services for authentication, hosting, databases, and server execution. In particular, Firebase Callable Functions make it trivial to serve authenticated code with JSON payloads. The client passes auth tokens to the server while the server code will validate it and get you the user id before your code even starts.

However, this does mean the way you call these functions from the client aren’t going to be the same as making a standard HTTP request.

Normally this isn’t a problem, but my web app also includes the ability to optionally take photos and upload them as part of the checkin. How do I upload a file to Google Cloud Storage through a Firebase Function?

To accomplish this, I used two StackOverflow posts [1] [2] and combined them to get an end-to-end working process. It was a bit more convoluted than just sticking the two answers together, so I’ll walk through what I did. Hopefully someone will find this useful, even if it’s just future me.

In my web app, I am using the Polymer framework and have installed many components to make available to me. One component is the paper-input-file which is where the user selects the file.

In my submission function, I read the image data and convert it to a base64 value.

This completes my check-in and returns the checkin ID, which is a unique ID from Firestore. Later on in the function I can use this to create a share dialog, so they can post it to other social networks.

You’ll notice the httpsCallable in the middle of this code, which sends all of the parameters. The fileData field is generated by the readFile function and can optionally be undefined.

After reading StackOverflow question [1] and several others, I finally arrived at an implementation that worked without OOM errors.

I don’t 100% understand what is going on here, other than converting the binary image data to base64. This encapsulated function returns the string as the resolution to a promise, making it easy to call from my sendCheckin function above.

Now that I’ve sent the base64 data to my Firebase Function, how do I store it? My checkin function does a lot of things: store a ‘Checkin’ object in a database, optionally upload my photo, add it to my feed, add it to the feed of my friends, and update the metrics for the entity I’ve checked into. For this post, I’m going to focus only on the most relevant part of the execution.

My project ID is determined by the Service Account Key (sak) I’ve downloaded. You see that I open up a storage bucket, give the file a random name based on two random floats, then update my checkin object with the filename to post in my photos feed and reuse in other places.

This is probably not the best way to give the file a unique name, but I can always fix it later. As the photo is tied to the checkin, I can use a better approach in the future without having to worry about applying this retroactively.

I use a writeFile function that I’ve defined in a separate file, using some help taken from StackOverflow answer [2]. I did rewrite their answer in a way that it is a Promise that can be easily integrated into a larger application.

You see that I take the string content, convert it back to binary data from base64, and throw it into a stream that writes the data to the file that I have defined.

This approach works. I’m sure there are some potential pitfalls. However, the benefits are pretty good. I’m able to store the file in a way that can be associated with the user by taking advantage of Firebase services without a lot of manual rewiring.

This works for any binary data, or at least it should. I imagine that there’s some upper limit based on memory limitations, but if photos work you can be fairly confident on what kinds of stuff you can do.

--

--

Nick Felker

Social Media Expert -- Rowan University 2017 -- IoT & Assistant @ Google