Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uploading files from Firebase Cloud Functions to Cloud Storage

The documentation is too complex for me to understand. It shows how to download a file from Cloud Storage to Cloud Functions, manipulate the file, and then upload the new file to Cloud Storage. I just want to see the basic, minimum instructions for uploading a file from Cloud Functions to Cloud Storage. Why doesn't this work:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp();

exports.storage = functions.firestore.document('Test_Value').onUpdate((change, context) => {

  var metadata = {
    contentType: 'text',
  };

  admin.storage().ref().put( {'test': 'test'}, metadata)
  .then(function() {
    console.log("Document written.");
  })
  .catch(function(error) {
    console.error(error);
  })

});

The error message is admin.storage(...).ref is not a function. I'm guessing that firebase-admin includes Firestore but not Storage? Instead of firebase-admin should I use @google-cloud/storage? Why doesn't this work:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

const {Storage} = require('@google-cloud/storage')();
const storage = new Storage();

admin.initializeApp();

exports.storage = functions.firestore.document('Test_Value').onUpdate((change, context) => {

  storage.bucket().upload( {'test': 'test'} , {
    metadata: {
      contentType: 'text'
    }
  })

});

I can't even deploy this code, the error message is

Error parsing triggers: Cannot find module './clone.js'

Apparently a npm module dependency is missing? But the module isn't called clone.js? I tried requiring child-process-promise, path, os, and fs; none fixed the missing clone.js error.

Why does admin.initializeApp(); lack parameters, when in my index.html file I have:

firebase.initializeApp({
    apiKey: 'swordfish',
    authDomain: 'myapp.firebaseapp.com',
    databaseURL: "https://myapp.firebaseio.com",
    projectId: 'myapp',
    storageBucket: "myapp.appspot.com"
  });

Another issue I'm seeing:

npm list -g --depth=0       

/Users/TDK/.nvm/versions/node/v6.11.2/lib
├── [email protected]
├── UNMET PEER DEPENDENCY  error: ENOENT: no such file or directory, open '/Users/TDK/.nvm/versions/node/v6.11.2/lib/node_modules/firebase-admin/package.json
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

In other words, there's something wrong with firebase-admin, or with Node 6.11.2. Should I use a Node Version Manager to revert to an older version of Node?

like image 937
Thomas David Kehoe Avatar asked Nov 04 '18 18:11

Thomas David Kehoe


People also ask

How do I upload files to the cloud Storage?

Drag and drop the desired files from your desktop or file manager to the main pane in the Google Cloud console. Click the Upload Files button, select the files you want to upload in the dialog that appears, and click Open.

How do I upload folders to Firebase Storage?

There is no way to upload an entire folder to Cloud Storage for Firebase in one go. You will have to upload the individual files in the folder. The is no concept of an empty folder in Cloud Storage for Firebase. Folders only exist by the fact that they have files in them.


1 Answers

  1. Go to https://console.cloud.google.com/iam-admin/iam
  2. Click the pencil icon next to your App Engine default service account
  3. + ADD ANOTHER ROLE
  4. 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.

  1. Go to Service Accounts and generate a private key
  2. Put the JSON file in your functions folder (next to src and node_modules)
  3. Go to Storage and copy the URL not including the "gs://" in the front. Use this for the storage bucket url when initializing admin.
  4. Use your project ID above for the database URL.
like image 64
Markymark Avatar answered Oct 18 '22 21:10

Markymark