firebase-admin-node icon indicating copy to clipboard operation
firebase-admin-node copied to clipboard

[FR] Admin 'getDownloadURL' for Storage

Open samtstern opened this issue 4 years ago • 5 comments

Is your feature request related to a problem? Please describe.

Download URLs are one of the most convenient ways to display content from Cloud Storage for Firebase, but right now they can only be created on the client.

There has been extensive discussion of this on the Cloud side, and their basic stance is "we don't like the Firebase URLs and we don't think you should be able to make them": https://github.com/googleapis/nodejs-storage/issues/697

As a result the best documentation on how to make these URLs on the server is this massive StackOverflow answer: https://stackoverflow.com/questions/42956250/get-download-url-from-file-uploaded-with-cloud-functions-for-firebase/56010225#56010225

Describe the solution you'd like

A simple getDownloadURL() method in the Node.js Admin SDK

Describe alternatives you've considered

None

Additional context

None

samtstern avatar Jun 30 '21 14:06 samtstern

I am also struggling with this and the community as well, we can see it on stackoverflow...

I've created my own way to get one following the described method in your shared link.

I think it's not right as one commenter from Google said, but I don't see another way?

https://stackoverflow.com/questions/70563169/resizing-images-already-on-google-storage-locally-through-sharpjs-and-keeping-u

Thank you in advance for supporting this issue!

spotvin42 avatar Jan 24 '22 09:01 spotvin42

I found an interesting article regarding this

https://www.sentinelstand.com/article/guide-to-firebase-storage-download-urls-tokens

ArnabXD avatar May 18 '22 05:05 ArnabXD

Well I've got a new job but still running into this same issue, we'd love a simple way to generate download URLs for objects from our backend Node.js server. Right now we can only do this on our frontend.

I do understand there are many workarounds but we'd like the solution to be the same on frontend and backend, not have different types of URLs depending on where they were generated.

samatcolumn avatar May 18 '22 16:05 samatcolumn

Yeah it's a bit ridiculous that we can't directly access the tokened URL from the Admin SDK. We want the token feature so we have the ability to revoke, as opposed to just making everything public. Ideally we would keep the URL in the associated Firestore document that we're already fetching on the client so we don't need to make extra round trip requests that needlessly slows down the application. I'm not sure why there has been so much pushback from Google on this- it feels like a major handicap to their Storage product that only the client SDKs can access the URL.

willbattel avatar May 18 '22 20:05 willbattel

We're using relatively old versions of Firebase (JS v8.3.3, Admin v9.4.2) so YMMV but I found that this workaround seems to get me what I want on the backend:

  const storage = admin.storage();
  const ref = storage.bucket(`gs://${bucket}`).file(pathToFile);
  const [metadata] = await ref.getMetadata();

  const token = metadata.metadata.firebaseStorageDownloadTokens;
  const link = `https://firebasestorage.googleapis.com/v0/b/${bucket}/o/${encodeURIComponent(
    pathToFile
  )}?alt=media&token=${token}

But it scares me that firebaseStorageDownloadTokens is undocumented and I can't figure out where that comes from or if it's guaranteed to exist in all situations (I assume not). I'd love to hear a Googler's perspective on this workaround at least.

samatcolumn avatar May 19 '22 16:05 samatcolumn

@lahirumaramba is this being looked at internally? Any updates?

atif7865 avatar Nov 16 '22 00:11 atif7865

Looks like they are open to it in principle but are looking to change the mechanism internally this year so I guess we'll just have to wait and see what that means...

See: https://github.com/firebase/firebase-admin-node/pull/2036#pullrequestreview-1246148110

willbattel avatar Apr 16 '23 20:04 willbattel

Hi, Firebaser here. Sorry for the radio silence and thanks for the continued interest in this FR. @willbattel is correct in that we are looking to offer alternatives to download urls in the future as there are some interesting security implications of this feature that makes it unideal in security-critical applications.

That being said, while we can't provide an estimated timeline on when it will become available, @maneesht is actively working on adding getDownloadUrl functionality to the admin SDK to bridge the gap in the meantime.

tonyjhuang avatar Apr 17 '23 00:04 tonyjhuang

Would be amazing if this PR#2036 gets merged soon👌

dellumdeus avatar Apr 21 '23 10:04 dellumdeus

Thank you @maneesht!!! Been wanting this feature for two years.

samatcolumn avatar Jun 08 '23 00:06 samatcolumn

Is this released? If so how do we use it? The documentation don't mention this.

MorenoMdz avatar Jul 04 '23 15:07 MorenoMdz

Is this released? If so how do we use it? The documentation don't mention this.

import admin from 'firebase-admin';
import { getDownloadURL } from 'firebase-admin/storage';

const bucket = admin.storage().bucket('your-storage-bucket-url.appspot.com');
const file = bucket.file('path');
const downloadUrl = await getDownloadURL(file);

console.log(downloadUrl);

Whimfoome avatar Jul 21 '23 13:07 Whimfoome

Thanks for the code sample @Whimfoome, our official docs have also been updated to include an example: https://firebase.google.com/docs/storage/admin/start#shareable_urls

tonyjhuang avatar Jul 21 '23 13:07 tonyjhuang

I'm getting this massive error `ApiError: Not Found.

 at new ApiError (/functions/node_modules/firebase-admin/node_modules/@google-cloud/storage/build/src/nodejs-common/util.js:80:15)
 at Util.parseHttpRespBody (//functions/node_modules/firebase-admin/node_modules/@google-cloud/storage/build/src/nodejs-common/util.js:215:38)
 at Util.handleResp (/home/hp/projects/functions/node_modules/firebase-admin/node_modules/@google-cloud/storage/build/src/nodejs-common/util.js:156:117)
 at /home/hp/projects/node_modules/@google-cloud/storage/build/src/nodejs-common/util.js:538:22
 at onResponse (/hom/functions/node_modules/retry-request/index.js:240:7)
 at /d/functions/node_modules/firebase-admin/node_modules/teeny-request/build/src/index.js:226:13
 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {

code: 404, errors: undefined, response: PassThrough { _events: { close: undefined, error: [ [Function (anonymous)], [Function (anonymous)] ], prefinish: [Function: prefinish], finish: undefined, drain: undefined, data: [Function (anonymous)], end: [Function (anonymous)], readable: undefined, unpipe: undefined }, _readableState: ReadableState { highWaterMark: 16384, buffer: [], bufferIndex: 0, length: 0, pipes: [], awaitDrainWriters: null, [Symbol(kState)]: 194512764 }, _writableState: WritableState { highWaterMark: 16384, length: 0, corked: 0, onwrite: [Function: bound onwrite], writelen: 0, bufferedIndex: 0, pendingcb: 0, [Symbol(kState)]: 1091466620, [Symbol(kBufferedValue)]: null }, allowHalfOpen: true, _maxListeners: undefined, _eventsCount: 4, statusCode: 404, statusMessage: 'Not Found', request: { agent: Agent { _events: [Object: null prototype] { free: [Function (anonymous)], newListener: [Function: maybeEnableKeylog] }, _eventsCount: 2, _maxListeners: undefined, defaultPort: 443, protocol: 'https:', options: [Object: null prototype] { maxSockets: Infinity, keepAlive: true, noDelay: true, path: null }, requests: [Object: null prototype] {}, sockets: [Object: null prototype] {}, freeSockets: [Object: null prototype] { 'firebasestorage.googleapis.com:443:::::::::::::::::::::': [ [TLSSocket] ] }, keepAliveMsecs: 1000, keepAlive: true, maxSockets: Infinity, maxFreeSockets: 256, scheduling: 'lifo', maxTotalSockets: Infinity, totalSocketCount: 1, maxCachedSessions: 100, _sessionCache: { map: {}, list: [] }, [Symbol(shapeMode)]: false, [Symbol(kCapture)]: false },`

I had to remove a few parts in the url for security. I'm using busboy to upload the file to the backend and then I'm trying to use getDownloadUrl(fileref) to get a url.

samasthwafer avatar Feb 23 '24 17:02 samasthwafer

@samasthwafer that's an API error due to the object not being found. Make sure your platform (emulator or cloud) and the file actually exist.

davit-b avatar Mar 18 '24 02:03 davit-b