Uploading photos to Google Cloud Storage through Firebase Callable Functions
The opinions stated here are my own, not those of my company.
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.
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   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 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  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 . 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.