How to getDownloadURL in a Firebase Function
Asked Answered
A

3

5

When using Firebase Storage to store images, there is a URL to the image that looks like this : https://firebasestorage.googleapis.com/v0/b/[MY-APP].appspot.com/o/[FILE-NAME]?alt=media&token=[TOKEN]

I want to get this URL.

According to this, this, and this and this, I need to use the .getDownloadURL() method, which is on the "storage ref" .. but the documentation of the available objects does not fit with the actual object.

And when I attempt to access the .getDownloadURL() method on the objects suggested by the documentation in the code below I get various property not found errors.

        const admin = require('firebase-admin');
        admin.initializeApp();

        admin
        .storage()
        .bucket()
        .upload(imageToBeUploaded.filepath, {
            destination: storageFolder,
            resumable: false,
            metadata: {
                metadata: {
                contentType: imageToBeUploaded.mimetype
                }
        }
        })
        .then((taskSnapshot) => {
            // HOW DO I GET THE DOWNLOADABLE URL 
        })

I've tried the following :

taskSnapshot[1].getDownloadURL(),

admin.storage().ref(storageFolder+'/'+imageFileName).getDownloadURL(),

admin.storageRef(storageFolder+'/'+imageFileName).getDownloadURL(),

admin.storage().ref().child(storageFolder+'/'+imageFileName).getDownloadURL(),

.. and a bunch of others.

Trial and error is not finding the "storageRef" that has that method,

How can I get the downloadable url for my image ?

Armet answered 4/11, 2019 at 7:50 Comment(0)
A
9

The solution I've used is to first change the access rules for my storage bucket, like so :

https://console.firebase.google.com/u/0/project/[MY_APP]/storage[MY_APP].appspot.com/rules

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read;
      allow write: if request.auth != null;
    }
  }
}

which means the token is not required on the URL in order to be able to view the image.

And then I have just hardcoded the URL :

            .then((taskSnapshot) => {
                const imageUrl = `https://firebasestorage.googleapis.com/v0/b/` +
                                 `${MY_APP_ID}.appspot.com/o/${imageFileName}?alt=media`;
                return imageUrl;
            })
Armet answered 4/11, 2019 at 10:55 Comment(2)
This is for client right? User is asking for functions.Harmonic
This sets the access to the files themselves. So, yes, the client code could then access them if this is done. However the code snippet share is actually backend function code that returns the URL to the client. ... I hope that answers your question?Armet
M
3

The Firebase Admin SDK wraps the Google Cloud Storage SDK, so their APIs are the same. The Cloud Storage SDK doesn't offer download URLs that are exactly like the ones provided by the mobile and web SDKs.

What you can do instead is generate a signed URL, which is functionally similar.

See also: Get Download URL from file uploaded with Cloud Functions for Firebase

Men answered 4/11, 2019 at 10:4 Comment(1)
Be careful: there's a big issue with using signed urls that you generate in a Firebase Function. You can read more at github.com/googleapis/nodejs-storage/issues/244 but the short version is: even if you specify an expiry of a day it may expire before then and all your links break, because of the service account's private keys are rotated automatically and that breaks the signed url. One workaround to this frustrating problem is apparently to stop using serviceAccountId and go back to json certs, ugh.Godsey
A
1

For anybody else that stumbled onto this, the following code works for me. The other answers didn't work for my usage, as I didn't want a link that expired.

I'm still learning JS and programming in general so I'm sure this code can be optimized further, for example:

UploadResponse contains both a File AND a Metadata object but I couldn't figure out how to filter the metadata directly and had to use the File in order to get its metadata.

const uploadResponse: storage.UploadResponse = await bucket.upload(tempFilePath);
for (const value of uploadResponse) {
  if (value instanceof storage.File) {
    const metadata = await value.getMetadata();
    const downloadUrl: string = metadata.shift().mediaLink;
  }
}
fs.unlinkSync(tempFilePath);
Aniline answered 11/11, 2020 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.