11

I use firebase-admin and firebase-functions to upload a file in Firebase Storage.

I have this rules in storage:

service firebase.storage {
  match /b/{bucket}/o {
    match /images {
      allow read;
      allow write: if false;
    }
  }
}

And I want get a public URL with this code:

const config = functions.config().firebase;
const firebase = admin.initializeApp(config);
const bucketRef = firebase.storage();

server.post('/upload', async (req, res) => {

  // UPLOAD FILE

  await stream.on('finish', async () => {
        const fileUrl = bucketRef
          .child(`images/${fileName}`)
          .getDownloadUrl()
          .getResult();
        return res.status(200).send(fileUrl);
      });
});

But I have this error .child is not a function. How can I get the public url of a file with firebase-admin?

KENdi
  • 7,576
  • 2
  • 16
  • 31
SaroVin
  • 1,583
  • 3
  • 23
  • 46

3 Answers3

13

From the sample application code on the using Cloud Storage documentation, you should be able to implement the following code to obtain the public download URL after the upload is successful:

// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(req.file.originalname);
const blobStream = blob.createWriteStream();

blobStream.on('finish', () => {
    // The public URL can be used to directly access the file via HTTP.
    const publicUrl = format(`https://storage.googleapis.com/${bucket.name}/${blob.name}`);
    res.status(200).send(publicUrl);
});

Alternatively, if you need a publicly accessible download URL, see this answer which suggests using getSignedUrl() from the Cloud Storage NPM module because the Admin SDK doesn't support this directly:

You'll need to generate a signed URL using getSignedURL via the @google-cloud/storage NPM module.

Example:

const gcs = require('@google-cloud/storage')({keyFilename: 'service-account.json'});
// ...
const bucket = gcs.bucket(bucket);
const file = bucket.file(fileName);
return file.getSignedUrl({
  action: 'read',
  expires: '03-09-2491'
}).then(signedUrls => {
  // signedUrls[0] contains the file's public URL
});
Grimthorr
  • 6,856
  • 5
  • 41
  • 53
  • 1
    Yes @Grimthorr but the Anonymous users does not have access at the file. – SaroVin Nov 02 '17 at 11:34
  • 1
    Ah, sorry, you're looking to obtain the public-access download URL instead? See the answer at - [Get Download URL from file uploaded with Cloud Functions for Firebase](https://stackoverflow.com/q/42956250/2754146) - it seems it's not possible with just the Admin SDK. – Grimthorr Nov 02 '17 at 11:41
  • yes, I know this way but I don't like this solution because the url is very long. Anyway, this seems to be the only way. – SaroVin Nov 02 '17 at 13:35
  • 1
    Unfortunately I think that is the only route due to limitations with the admin SDK. I guess you could pass the URL through [Google's URL shortener API](https://developers.google.com/url-shortener/v1/getting_started) but that adds yet another step. I've updated my answer with details from the other one. – Grimthorr Nov 02 '17 at 14:28
  • 1
    Admin SDK uses `@google-cloud/storage` underneath. The bucket object returned by `admin.storage().bucket()` comes from that package. So you don't have to re-initialize the GCS package. Provided you have initialized admin SDK with a service account, you should be able to call `getSignedUrl()` on the file references obtained from the API. – Hiranya Jayathilaka Nov 02 '17 at 17:13
5

What worked for me is to compose a URL like this:

https://storage.googleapis.com/<bucketName>/<pathToFile>

Example: https://storage.googleapis.com/mybucket.appspot.com/public/myFile.png

How I found it?

I went to GCP Console, Storage. Located the uploaded file. Clicked "Copy URL".

You may want to make a file Public first. I did it like this:

const bucket = seFirebaseService.admin().storage().bucket()
await bucket.file(`public/myFile.png`).makePublic()
Kirill Groshkov
  • 1,535
  • 1
  • 22
  • 23
  • This is the correct way, and it allows you to access the file using an ajax request for e.g. JSON data. – holmberd Apr 24 '21 at 19:46
  • This also worked for me and is much simpler than the accepted solution. After the makePublic() is applied to the file the URL returned from publicUrl() can be accessed publicly and anonymously. – Gorgant May 25 '22 at 01:00
2

I've been tinkering with this for days and realized

A) correct access rights on the bucket is key:

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

B) The functional public URL is right there in the meta data (tested and works). Notice the access rights.

  const pdfDoc = printer.createPdfKitDocument(docDefinition);
  const pdfFile = admin
      .storage()
      .bucket()
      .file(newId + '.pdf');

    pdfDoc.pipe(
      pdfFile.createWriteStream({
        contentType: 'application/pdf',
        public: true,
      })
    );
    pdfDoc.end();

    console.log('Get public URL');
    const publicUrl = pdfFile.metadata.mediaLink;
Thomas Hagström
  • 4,371
  • 1
  • 21
  • 27