Firebase Storage downloadURL or downloadURLs? - firebase

When I work with Firebase Storage I use downloadURL file metadata to access the file. But the documentation says it's downloadURLs. Is that a typo?

It says that downloadURLs is an Array of strings.
From experience, Firebase lets you create new Download Urls so that single files can have multiple download URLs.

It is mentioned on the documentation that the downloadURLs variable is:
An array of long-lived download URLs. Always contains at least one
URL.
downloadURL is a different variable. If you get the downloadURL programmatically using getDownloadUrl(), it will generate a new url that will be added to the downloadURLs array. But if you get it from the metadata, it will give you an URL that already exists on that array.

Related

Firebase emulator hitting DB via the REST feature

I’m trying to setup the emulator so I can develop the firebase functions safely before deploying them. I just noticed that some REST calls I’m doing now fails - anybody know if it is not possible to use the REST feature of the RealTime DB https://firebase.google.com/docs/reference/rest/database
I'm trying to hit it with this URL
http://localhost:9000/?ns=<PROJECT ID>-default-rtdb/development/DISHES.json
because this is what I set the firebaseConfig.databaseURL to (suggested here by Google)
Bonus info: If I try to do a GET to the URL via postman it creates another database called fake-server (http://localhost:4000/database/fake-server: null) 🤔
According to RFC 3986, the path must come before the query parameters in URLs. Your URL should be instead written as:
http://localhost:9000/development/DISHES.json?ns=<PROJECT ID>-default-rtdb
Note how the corrected URL has the query parameter appended to the very end. (The URL you've provided in the question will be parsed as having one query parameter ns with the value of <PROJECT ID>-default-rtdb/development/DISHES.json, which is not a valid namespace name. That explains the errors you've seen.)
FYI, it looks like you're constructing the URL by concatenating the string databaseURL with the path -- this may lead to surprising results as you've seen above. Considering using a URL parser / formatter in your language / framework of choice instead, which handles URL parts correctly. For example, in JavaScript you can use the snippet below:
const url = new URL(databaseURL); // Parse URL (including query params, if any)
url.pathname = '/development/DISHES.json';
yourFetchingLogic(url.toString()); // Reconstruct the URL with path replaced

Get picture from Firebase Storage and show it to the user Flutter

If I have a reference for Firebase Storage saved as String, like :
String reference = https://firebasestorage.googleapis.com/v0/b/something/o/bucket%2Fp%2FprofilePicture%2Fimage_picker_26.jpg?alt=media&token=something
And this reference points to a picture, how can I get this image from Storage and show it to the user?
I was thinking something like :
Image.network(reference)
But I read on the internet that this isn't safe and I think that this isn't so easy...I should have something like : FirebaseStorage.instance.refFromURL(url) before calls any widget to show it.
Do you have any suggestion?
I understand that the problem is that the URL returned by the getDownloadURL() method is a long lived one. This method does not offer the possibility to configure an expiration date to the download URL. It is the same with the JS Client SDK.
However, it is possible with the Admin SDK, see for example the getSignedUrl() method of the Node.js Admin SDK.
So you could create a Callable Cloud Function, that you call by passing the file reference and which returns a signedURL with an expiration date you choose.
From your Flutter app, you would call this Cloud Function as explained here.
If you want to get the url from the storage after upload then do:
FirebaseStorage storage = FirebaseStorage.instance;
Reference ref = storage.ref().child("folder");
UploadTask uploadTask = ref.putFile(image);
uploadTask.then((res) {
res.ref.getDownloadURL();
});
To display it you can use Image.network()

Send firebase storage authorization as url parameter from a flutter web app

I would like to know how to make an authorized request to firebase storage using the user Id Token as a parameter in the url. Right now with a firebase rule of 'request.auth != null' I receive a 403 network error (Failed to load video: You do not have permission to access the requested resource). Here is my GET request url:
https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<folder_name>%2F<video_name>.mp4?alt=media&auth=eyJh...<ID TOKEN>...Ll2un8ng
-WITHOUT the firebase rule in place I'm able to successfully get the asset with this request url https://firebasestorage.googleapis.com/v0/b/<bucket>/o/<folder_name>%2F<video_name>.mp4?alt=media
-also tried token=, token_id=, tokenId=
-the reason for not using the firebase SDK to fetch the file is so that I can use the flutter video_player (https://pub.dev/packages/video_player#-example-tab-) package and use this with files in firebase, I mention this in case theres a better way to use the video_player library in flutter web right now:
_controller = VideoPlayerController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
closedCaptionFile: _loadCaptions(),
);
[EDIT] It appears that it's not possible to pass the auth in as a query parameter. After some exploring, I found an acceptable way to still use the video_player with your firebase assets that are protected (If you're not using rules to protect them, you can directly use the firebase url). I will post some general steps here and some sample code:
Use the Storage Firebase SDK package to get the Uint8List, the uri given by getDownloadURL has the correct header auth, for example
import 'package:firebase/firebase.dart';
final url = await storagePath.getDownloadURL();
final response = await http.get(url);
if (response.statusCode == 200) {
return response.bodyBytes;
}
use the Uint8List buffer to init a Blob object which you'll use to then create an ObjectURL which basically gives you the same interface as a file url to use as the network url for your video player
final blob = html.Blob([data.buffer], 'video/mp4');
final videoUrl = html.Url.createObjectUrl(blob);
videoPlayerController = VideoPlayerController.network(
videoUrl)
..initialize().then((_) {...
That's it.
Firebase Storage REST does not (rightly) support authorization from GET query string as you are trying to do. Instead, it uses the standard Authorization header (see here).
Firebase cloud storage internally uses Google Cloud Storage. Mentioned here
If the library you use doesn't support HTTP headers yet, you must consider an alternative. The issue you mentioned in the comment shows that the feature is still under development, so you can also wait for the library to come out with the support for headers.
Internally all this package does for flutter-web is create an HtmlElementView widget here for which it passes a VideoElement (ref here) from the package dart:html with the provided URL which translates to a <Video> tag inside a shadow dom element in your web page. The error 403 could also mean you are trying to access it from a different origin.
I would suggest following approach.
Check your console for any CORS related errors. If yes, then you will have to whitelist your ip/domain in the firebase storage. Check this post for possible approach and more details here.
Check if you are able to access the URL directly with the authorization token as a query parameter as you suggested. If not then, it is not the correct way to access the object and should be corrected. You could update the question with the exact error details.

How should I save references to firebase storage objects?

Store references by name (users/1/profile.png). Then the URL needs to be generated all the time.
const url = await firebase
.storage()
.ref('users/1/profile.png')
.getDownloadURL()
Store references by URL. The access token could be revoked which would cause issues trying to generate a new one and update it in the database.
const url = await firebase
.storage()
.refFromURL(invalidURL)
.getDownloadURL()
Related to #1. Store by file name only so files can be moved without having to update database references.
const url = await firebase
.storage()
.ref(`users/${user.id}/${user.image}`)
.getDownloadURL()
The download URL and the reference path are two different things and I'd store each of them as appropriate (and sometimes both).
Store the Download URL when you want to directly serve the file from storage (e.g. an <img> tag).
Store the Reference Path when you need to keep a reference to the file to modify it later.
Calling getDownloadURL() does trigger a network request and so it's advisable to cache the result when possible to avoid unnecessary extra work/latency.

Get URL from firebase storage file

I want to get a downloadURL from a file inside my storage from within the onFinalize trigger. In a best case scenario, I want an URL as short as possible (so preferably not a signed one, but just one like the public one like it can be seen in the Firebase Storage UI). Keep in mind, that I am moving the file first, so I cannot access it directly from the onFinalize parameter.
I currently have the following solution:
await imageRef.move(newPath);
const newFile = defaultBucket.file(newPath);
const url = (await newFile.getSignedUrl({
action: 'read',
expires: '03-09-2491'
}))[0];
This approach has two flaws:
Apparently the signed URL is only valid for 3 days. This may be a known issue
The URL is very long and takes much space in my Firestore
I also saw an approach, where the URL is being reproduced from the bucket name and the token, but I did not manage to find the token in the metadata of the file.

Resources