File upload with filestreams and Firebase cloud functions + cloud storage
Asked Answered
G

5

6

I've written the following code for my QR file upload using firebase cloud functions

const functions = require('firebase-functions');
const qrcode = require('qrcode')
const admin = require('firebase-admin');
const spawn = require('child-process-promise').spawn;
const serviceAccount = require("./secret.json");
const gcs = require('@google-cloud/storage')();

admin.initializeApp(functions.config({
  credential: admin.credential.cert(serviceAccount),
  storageBucket: "https://SECRET.firebaseio.com"
}));


exports.qrcode = functions.https.onRequest((req, res) => {
  const storage = admin.storage().bucket();
  const dummyFile = storage.file('dummy.png')
  new Promise ((resolve, reject) => qrcode.toFileStream(
    dummyFile.createWriteStream()
      .on('finish', resolve)
      .on('error', reject),
    'DummyQR'))
    .then(console.log("success")) //Doing stuff here later
    .catch(console.log)
  res.send("<script>window.close();</script>")
});

According to the docs I should be able to connect to the bucket by simply calling admin.storage().bucket(); (https://firebase.google.com/docs/storage/admin/start) however I get the following error:

Error: Error occurred while parsing your function triggers.

Error: Bucket name not specified or invalid. Specify a valid bucket name via the storageBucket option when initializing the app, or specify the bucket name explicitly when calling the getBucket() method.

and so I'm stuck and not sure how to proceed. If I try to manually enter the bucket admin.storage().bucket("https://SECRET.firebaseio.com"); I get the error

{ ApiError: Not Found
    at Object.parseHttpRespBody (/user_code/node_modules/firebase-admin/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:193:30)
    at Object.handleResp (/user_code/node_modules/firebase-admin/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:131:18)
    at /user_code/node_modules/firebase-admin/node_modules/@google-cloud/storage/node_modules/@google-cloud/common/src/util.js:496:12
    at Request.onResponse [as _callback] (/user_code/node_modules/firebase-admin/node_modules/retry-request/index.js:195:7)
    at Request.self.callback (/user_code/node_modules/firebase-admin/node_modules/request/request.js:185:22)
    at emitTwo (events.js:106:13)
    at Request.emit (events.js:191:7)
    at Request.<anonymous> (/user_code/node_modules/firebase-admin/node_modules/request/request.js:1157:10)
    at emitOne (events.js:96:13)
    at Request.emit (events.js:188:7)
  code: 404,
  errors: [ { domain: 'global', reason: 'notFound', message: 'Not Found' } ],
  response: undefined,
  message: 'Not Found' }
Giro answered 7/6, 2018 at 15:33 Comment(0)
A
20

I had this same problem. I just added the storageBucket name when initializing the app, and if you are using the getSignedUrl method you need to include a service account, otherwise that url it's gonna get expired after a week (as it was in my case).

const serviceAccount = require('/your/service/account/key/path');
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    storageBucket: "your-storage-bucket-name.appspot.com",
});

don't forget to update your firebase functions with

firebase deploy --only functions

on the command line

UPDATE 6 APRIL 2020

Signed URLs actually expire after 7 days. If you need URLs for more time than that, you should read this answer and this thread

Appellant answered 10/3, 2019 at 22:18 Comment(3)
This should be the accepted answer, or the accepted answer should make clear that you should set your credentials in some other manner.Cadent
@SpencerWilliams Again, you should understand the behavior of the default service account provided for your project by Cloud Functions.Annemarie
yes that work for me i add storageBucket: "bucket name" and it workNicolettenicoli
A
3

It looks like you're not initializing the admin SDK correctly. Just call initializeApp with no parameters to get all the correct defaults:

admin.initializeApp();

This will use the default service account provided by Cloud Functions for your project. This account has permission to do most of what you need to do in your function without any additional configuration.

Annemarie answered 7/6, 2018 at 16:32 Comment(9)
Correct but I did need to add the storageBucket as follows: admin.initializeApp({ credential: admin.credential.cert(serviceAccount), storageBucket: "https://XXX.firebaseio.com" });Giro
You shouldn't need to add the storage bucket if you're just accessing the default bucket for the project.Annemarie
Odd, I tried it but then I get the same error as beforeGiro
I write functions all the time that use Cloud Storage via the admin SDK, and I've never had to configure anything. The one exception is that you need to init with a service account in order to call getSignedUrl.Annemarie
Okay I tried a second time just to be and this time it worked with just admin.initializeApp();. Perhaps the firebase functions hadn't refreshed properly. Thank you for the help!Giro
@DougStevenson You most certainly do need the serviceAccount initialization unless you set your credentials in some other way. Otherwise, how will firebase know what project you are referring to? Or what client ID, private key, etc. to use? So if you have code like the OP has provided and credentials have not otherwise been set, you absolutely need that data as well as the storageBucket key for the object passed to initliazeApp.Cadent
@SpencerWilliams Cloud Functions provides the credentials for the default service account in your project. This is what's used when you don't specify credentials. This is OK for most cases. It's not until you try to reach outside the project, or constrain access to certain resources. If you take a look at the samples provided by Firebase, they almost all use the default credentials except in cases where it's not sufficient. github.com/firebase/functions-samplesAnnemarie
You would need to use serviceAccount.json when accessing the bucket from localhost when testing.Fernandofernas
@Fernandofernas When testing in the Firebase local emulator, it is not necessary. The environment will be set up so that the default service account will be used. If you want to run a standalone node script, you can direct the admin SDK to the service account by setting the shell environment before running the script. That will still allow a no-argument init to succeed.Annemarie
M
1

In my case it was similar issue to this one (Unable to use cloud storage without specifying storageBucket when initialising app with firebase-admin).

I had following setup:

const admin = require("firebase-admin")
admin.initializeApp()

export * from "./api/api"
export * from "./cron/cronJobs"
export * from "./posts/postsCloudFunctions"

Changed it to:

const admin = require("firebase-admin")

export * from "./api/api"
export * from "./cron/cronJobs"
export * from "./posts/postsCloudFunctions"

admin.initializeApp()

And it seems the issue is initializeApp() was not being called before accessing the storage bucket.

Mcburney answered 12/9, 2019 at 9:9 Comment(0)
N
0

I have same issue while uploading to image to firebase storage:

Error: Bucket name not specified or invalid. Specify a valid bucket name via the storageBucket option when initializing the app, or specify the bucket name explicitly when calling the getBucket() method. ndlers\users.js:130:8) at Busboy.emit (events.js:189:13) at Busboy.emit (E:\react-project\socialape-functions\functions\node_modules\busboy\lib\main.js:37:33) at E:\react-project\socialape-functions\functions\node_modules\busboy\lib\types\multipart.js:52:13 at process._tickCallback (internal/process/next_tick.js:61:11)

Then i add storageBucket to admin function

var admin = require("firebase-admin");
var serviceAccount = require("your service account key file path);

admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
  databaseURL: "https://your-app-1234.firebaseio.com",
  storageBucket: "your-storage-bucket-name given in firebase storage"
});

const db = admin.firestore();

module.exports = { admin, db };

After adding bucket to admin functions it works.

Nicolettenicoli answered 29/1, 2020 at 6:36 Comment(0)
C
0

You should change

storageBucket: "your-storage-bucket-name given in firebase storage"

to

storageBucket: "<BUCKET_NAME>.appspot.com".

You can find BUCKET_NAME as the picture below:

enter image description here

Cordeliacordelie answered 30/6, 2020 at 15:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.