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 ?

Community
  • 1
  • 1
kris
  • 11,868
  • 9
  • 88
  • 110

3 Answers3

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;
            })
kris
  • 11,868
  • 9
  • 88
  • 110
  • This is for client right? User is asking for functions. – avilao Jun 04 '23 at 11:37
  • 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? – kris Jun 05 '23 at 03:38
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

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
  • Be careful: there's a big issue with using signed urls that you generate in a Firebase Function. You can read more at https://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. – xaphod Oct 06 '20 at 18:48
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);
jassycliq
  • 26
  • 2