简体   繁体   中英

Google Cloud Function - Why bucket always returns empty array of files?

I'm trying to reach files stored on my Firebase Storage to return a list of them to my APK. I made a cloud function, which is not working, even though when everything seems to be right settle.

index.js

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

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

const images = require('./images.js');

const storage = new Storage({
    projectId: '<my_id>',
    keyFilename: './key.json',
});

const bucket = storage.bucket('<my_bucket>')

exports.getImages = functions.https.onRequest((request, response) => {
    images.getImages(bucket).then(urls => response.send(urls))
})

images.js

module.exports = {
    getImages
}

const query = {
    directory: '/images'
};

async function getImages(bucket) {
    return bucket.getFiles(query).then(files => {
        console.log(files)
        getUrls(files).then(urls => console.log(urls))
            .catch(() => console.error('URLs failure'));
    }).catch(() => console.error('Files failure'));
}

function getUrls(files) {
    const promises = []
    files.forEach(file => promises.push(file.getDownloadURL()))
    return Promise.all(promises);
}

My Key.json is located inside my functions folder

Key.json

{
  "type": "service_account",
  "project_id": "…”,
  "private_key_id": “…”,
  "private_key": "-----BEGIN PRIVATE KEY——…”,
  "client_email": “…”,
  "client_id": “…”,
  "auth_uri": “…”,
  "token_uri": “…”,
  "auth_provider_x509_cert_url": “…”,
  "client_x509_cert_url": “…”
}

Storage Rules

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if true
    }
  }
}

Executing getImages() function output is:

console.log(files): [ [] ]
console.error('Files failure'): Files failure

What am I doing wrong here, please?

First and main problem I had was connecting with my Google Cloud Service, this means:

  1. I generated a brand new private key from Firebase Admin

Firebase项目 项目设定 产生新金钥

  1. Downloaded file and settle on my project as my new Key.json

  2. Then comes the trickiest part. According to Google 's documentation

You can use the bucket references returned by the Admin SDK in conjunction with the official Google Cloud Storage client libraries to upload, download, and modify content in the buckets associated with your Firebase projects. Note that you do not have to authenticate Google Cloud Storage libraries when using the Firebase Admin SDK. The bucket references returned by the Admin SDK are already authenticated with the credentials used to initialize your Firebase app.

This means, if you are running you service locally ( npm run shell ), you need to instantiate your firebase-admin .

var serviceAccount = require("./key.json");
var admin = require("firebase-admin");

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://<my_project>.firebaseio.com"
});

and then calling

const bucket = admin.storage().bucket('<my_bucket>.appspot.com')

you will be ready to access your files. When accessing your google functions directly through deployment or from cloud console , then accessing your storage wouldn't require an admin initialization or the key itself such as the first exposed index.js on the question, turning the code only into something like

const {Storage} = require('@google-cloud/storage');
...
const storage = new Storage({
    projectId: '<my_id>',
});

const bucket = storage.bucket('<my_bucket>')
...

Regarding what Mayeru said:

you have specified the directory as "/images" but instead it should be "images/"

"images/" could be used for querying prefix parameters, not directory . This example shows how it works, like its reference to google's documentation as well.

The following code, with some promise tweaks and parsing JSON data for Android, works like a charm. Locally and remotely.

index.js

var serviceAccount = require("./key.json");
const functions = require('firebase-functions');

const images = require('./images.js');

var admin = require("firebase-admin");

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: "https://<my_project>.firebaseio.com"
});

const bucket = admin.storage().bucket('<my_bucket>.appspot.com')

exports.getImages = functions.https.onRequest((request, response) => {
    images.getImages(bucket)
        .then(urls => response.status(200).send({ data: { urls } }))
        .catch(err => console.error(err));
})

images.js

module.exports = {
    getImages
}

const query = {
    directory: 'images'
};

function getImages(bucket) {
    return bucket.getFiles(query)
        .then(response => getUrls(response))
        .catch(err => console.error(err));
}

function getUrls(response) {
    const promises = []
    response.forEach( files => {
        files.forEach (file => {
            promises.push(getSignedUrl(file));
        });
    });
    return Promise.all(promises).then(result => getParsedUrls(result));
}

function getSignedUrl(file) {
    return file.getSignedUrl({
        action: 'read',
        expires: '09-01-2019'
    })
}

function getParsedUrls(result) {
    return JSON.stringify(result.map(mediaLink => createMedia(mediaLink)));
}

function createMedia(mediaLink) {
    const reference = {};
    reference.mediaLink = mediaLink[0];
    return reference;
}

This will for sure list all the files under images folder from your bucket and access their signed download urls to be rendered with libraries like Fresco . Last, but not least, the download URL will be exposed, but to make a GET request directly on it will require adjustment on your bucket permissions. Anyway, this "permissions thing" is another topic which you might find how to do it also here on stack overflow.

The logs show:

[[]] // this is the "files" you retrieved.
Files failure 

As you can see, you are not retrieving any file, hence why it fails the call to get URLs from empty array.

you have specified the directory as "/images" but instead it should be "images/"

That should be able to retrieved the proper folder.

Also, the function "getDownloadURL()" don't appear as supported for "file" objects on nodejs lib documentation . You might be looking for getSignedUrl ?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM