Uploading files from Firebase Cloud Functions to Cloud Storage
See Introduction to the Admin Cloud Storage API for further details on how to use the Cloud Storage service in Firebase Admin SDK.
var admin = require("firebase-admin"); var serviceAccount = require("path/to/serviceAccountKey.json"); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), storageBucket: "<BUCKET_NAME>.appspot.com" }); var bucket = admin.storage().bucket(); // 'bucket' is an object defined in the @google-cloud/storage library. // See https://googlecloudplatform.github.io/google-cloud-node/#/docs/storage/latest/storage/bucket // for more details.
Regarding uploading objects, see Cloud Storage Documentation Uploading Objects sample code:
// Imports the Google Cloud client library const {Storage} = require('@google-cloud/storage'); // Creates a client const storage = new Storage(); /** * TODO(developer): Uncomment the following lines before running the sample. */ // const bucketName = 'Name of a bucket, e.g. my-bucket'; // const filename = 'Local file to upload, e.g. ./local/path/to/file.txt'; // Uploads a local file to the bucket await storage.bucket(bucketName).upload(filename, { // Support for HTTP requests made with `Accept-Encoding: gzip` gzip: true, metadata: { // Enable long-lived HTTP caching headers // Use only if the contents of the file will never change // (If the contents will change, use cacheControl: 'no-cache') cacheControl: 'public, max-age=31536000', }, }); console.log(`${filename} uploaded to ${bucketName}.`);
I uploaded a file from my hard drive to Firebase Cloud Storage via Google Cloud Functions. First, I found the documentation for Google Cloud Functions bucket.upload.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.Storage = functions.firestore.document('Storage_Value').onUpdate((change, context) => {
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
const bucket = storage.bucket('myapp.appspot.com');
const options = {
destination: 'Test_Folder/hello_world.dog'
};
bucket.upload('hello_world.ogg', options).then(function(data) {
const file = data[0];
});
return 0;
});
The first three lines are Cloud Functions boilerplate. The next line
exports.Storage = functions.firestore.document('Storage_Value').onUpdate((change, context) => {
creates the Cloud Function and sets the trigger. The next three lines are more Google Cloud boilerplate.
The rest of the code locates the file hello_world.ogg
on my computer's hard drive in the functions
folder of my project directory and uploads it to the directory Test_Folder
and changes the name of the file to hello_world.dog
in my Firebase Cloud Storage. This returns a promise, and the next line const file = data[0];
is unnecessary unless you want to do something else with the file.
Lastly we return 0;
. This line does nothing except prevent the error message
Function returned undefined, expected Promise or Value
- Go to https://console.cloud.google.com/iam-admin/iam
- Click the pencil icon next to your
App Engine default service account
+ ADD ANOTHER ROLE
- Add
Cloud Functions Service Agent
In my specific use case, I needed to decode a base64 string into a byte array and then use that to save the image.
var serviceAccount = require("./../serviceAccountKey.json");
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp({
projectId: serviceAccount.project_id,
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://your_project_id_here.firebaseio.com", //update this
storageBucket: "your_bucket_name_here.appspot.com" //update this
});
function uploadProfileImage(imageBytes64Str: string): Promise<any> {
const bucket = admin.storage().bucket()
const imageBuffer = Buffer.from(imageBytes64Str, 'base64')
const imageByteArray = new Uint8Array(imageBuffer);
const file = bucket.file(`images/profile_photo.png`);
const options = { resumable: false, metadata: { contentType: "image/jpg" } }
//options may not be necessary
return file.save(imageByteArray, options)
.then(stuff => {
return file.getSignedUrl({
action: 'read',
expires: '03-09-2500'
})
})
.then(urls => {
const url = urls[0];
console.log(`Image url = ${url}`)
return url
})
.catch(err => {
console.log(`Unable to upload image ${err}`)
})
}
Then you can call the method like this and chain the calls.
uploadProfileImage(image_bytes_here)
.then(url => {
//Do stuff with the url here
})
Note: You must initialize admin with a service account and specify the default bucket. If you simply do admin.initializeApp()
then your image urls will expire in 10 days.
Steps to properly use a service account.
- Go to Service Accounts and generate a private key
- Put the JSON file in your functions folder (next to src and node_modules)
- Go to Storage and copy the URL not including the "gs://" in the front. Use this for the storage bucket url when initializing admin.
- Use your project ID above for the database URL.