Cloud Storage for Firebase access error "admin.storage(...).ref is not a function"
Asked Answered
T

3

14

I am working with Cloud Storage for Firebase and can't figure out how to to access storage files

According to https://firebase.google.com/docs/storage/web/start official guide and https://firebase.google.com/docs/storage/web/create-reference this code should be returning root reference

let admin = require('firebase-admin')
admin.initializeApp({...})
let storageRef = admin.storage().ref()

But it throws an error saying

TypeError: admin.storage(...).ref is not a function

package.json

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {...},
  "dependencies": {
    "@google-cloud/storage": "^1.5.1",
    "firebase": "^4.8.0",
    "firebase-admin": "~5.4.2",
    "firebase-functions": "^0.7.1",
    "pdfkit": "^0.8.3",
    "uuid": "^3.1.0"
  },
  "private": true
}

node -v => v7.7.4

My end goal it to download files or upload a pdf file to storage.

Tapley answered 14/12, 2017 at 9:41 Comment(5)
are you on functions? if so, use bucket = admin.storage().bucket(), functions admin does not use the web apisHeriot
Thank you for replying, yes, I can access bucket this way and can access file using bucket.file('images/snow.jpg') but i want to return/download file in response of cloud function. Can you guide?Tapley
I have tried bucket.file('images/snow.jpg').getDownloadURL() but it says bucket.file(...).getDownloadURL is not a functionTapley
because its not. See available functions: cloud.google.com/nodejs/docs/reference/storage/1.4.x/FileHeriot
Hey .. I keep getting .. Error: Cannot sign data without client_email. where can I configure client_email ?Zeidman
P
23

You are using Cloud Functions and the Firebase Admin SDK to try and access your bucket. The Getting Started guide you cited is talking about client sided Web Apps with the Web API for Firebase not the Admin one. The functionality there is different, because it uses a different SDK (even if their names are the same).

The Storage Object you are trying to access doesn't have the ref() functions, only app and bucket().

https://firebase.google.com/docs/reference/admin/node/firebase-admin.storage

Try using the Google Cloud APIs directly:

https://cloud.google.com/storage/docs/creating-buckets#storage-create-bucket-nodejs

-

EDIT: This edit is only to stop scaring people with a post from two years ago. The answer above still applies as of May 2020.

Prepotent answered 20/5, 2018 at 15:16 Comment(6)
It's a perfect explanation for a common confusion. I think that Firebase's documentation could be much better. Your simple answer has made me save a lot of time. Thanks!Insomnolence
is that still true as of March 2020? If no, any plans to include storage api to the admin sdk?Lizliza
Still true as of APR 2020. Firebase's documentation could've highlighted this better.Shipshape
firebase.google.com/docs/reference/admin/node/… link is not found , could you plz update a alternative link ?Instrumentalist
firebase.google.com/docs/reference/admin/node/admin.storageJacklynjackman
Updated, thanks for letting me know :)Prepotent
M
3

In the below example, I am extracting image references from an existing Firestore collection called "images". I am cross referencing the "images" collection with the "posts" collection such that I only get images that relate to a certain post. This is not required.

Docs for getSignedUrl()

const storageBucket = admin.storage().bucket( 'gs://{YOUR_BUCKET_NAME_HERE}.appspot.com' )
const getRemoteImages = async() => {
    const imagePromises = posts.map( (item, index) => admin
        .firestore()
        .collection('images')
        .where('postId', '==', item.id)
        .get()
        .then(querySnapshot => {
            // querySnapshot is an array but I only want the first instance in this case
            const docRef = querySnapshot.docs[0] 
            // the property "url" was what I called the property that holds the name of the file in the "posts" database
            const fileName = docRef.data().url 
            return storageBucket.file( fileName ).getSignedUrl({
                action: "read",
                expires: '03-17-2025' // this is an arbitrary date
            })
        })
        // chained promise because "getSignedUrl()" returns a promise
        .then((data) => data[0]) 
        .catch(err => console.log('Error getting document', err))
    )
    // returns an array of remote image paths
    const imagePaths = await Promise.all(imagePromises)
    return imagePaths
}

Here is the important part consolidated:

const storageBucket = admin.storage().bucket( 'gs://{YOUR_BUCKET_NAME_HERE}.appspot.com' )
const fileName = "file-name-in-storage" // ie: "your-image.jpg" or "images/your-image.jpg" or "your-pdf.pdf" etc.
const remoteImagePath = storageBucket.file( fileName ).getSignedUrl({
    action: "read",
    expires: '03-17-2025' // this is an arbitrary date
})
.then( data => data[0] )
Michealmicheil answered 25/10, 2019 at 17:12 Comment(0)
S
1

If you simply want temporary links to all images in a folder in a Cloud Storage Bucket, the following code snippet will achieve it. In this example, I query for all images under the folder images/userId.

exports.getImagesInFolder = functions.https.onRequest(async (req, res) => {
    const storageRef = admin.storage().bucket('gs://{YOUR_BUCKET_NAME_HERE}');
    const query = {
        directory: `images/${req.body.userId}` // query for images under images/userId
    };
    
    const [files] = await storageRef.getFiles(query)
    const urls = await Promise.all(files.map(file => file.getSignedUrl({
        action: "read",
        expires: '04-05-2042' // this is an arbitrary date
    })))

    return res.send({ urls })
})

API Documentation:

PS: Please keep in mind that this might allow anyone to pass a userId and query for all images for this specific user.

Stonedeaf answered 14/5, 2021 at 10:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.