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' }
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
Marc
  • 626
  • 5
  • 15

5 Answers5

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

ihojmanb
  • 452
  • 8
  • 17
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.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • 1
    Correct but I did need to add the storageBucket as follows: ```admin.initializeApp({ credential: admin.credential.cert(serviceAccount), storageBucket: "https://XXX.firebaseio.com" });``` – Marc Jun 07 '18 at 18:25
  • 2
    You shouldn't need to add the storage bucket if you're just accessing the default bucket for the project. – Doug Stevenson Jun 07 '18 at 18:39
  • Odd, I tried it but then I get the same error as before – Marc Jun 07 '18 at 18:47
  • 1
    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. – Doug Stevenson Jun 07 '18 at 19:00
  • 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! – Marc Jun 07 '18 at 19:08
  • @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`. – Spencer Williams Aug 16 '19 at 19:57
  • @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. https://github.com/firebase/functions-samples/ – Doug Stevenson Aug 16 '19 at 20:07
  • You would need to use serviceAccount.json when accessing the bucket from localhost when testing. – KasparTr Dec 15 '19 at 09:07
  • @KasparTr 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. – Doug Stevenson Dec 15 '19 at 18:15
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.

atereshkov
  • 4,311
  • 1
  • 38
  • 49
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.

akshay_sushir
  • 1,483
  • 11
  • 9
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

Jason Aller
  • 3,541
  • 28
  • 38
  • 38