Firebase/Google Cloud Storage how to store the image - firebase

I have a Firestore that has a User Document. When a User uploads a profile image to the bucket I resize it in the Cloud Function and then save the smaller thumbnail of it. Now I am not sure what the best practice is to receive the Download URL for it, there are 2 possibilities:
In the Cloud Function get a Signed URL and store it in the Users Document or
Get the download url on the frontend with the .getDownloadUrl() method.
The problems I have with either solution is
1: The URL is really big, getting multiple Users on a Page this adds up in more size than the actual rest of the User Document
2: In terms of speed im not sure if its the best to loop through a list of Users to get each's thumbnail download URL, but the advantage is I do not have to deal with normalizing the new Profile Pic URL one every occurrence in the database.

The URL is not that that big. Before trying to optimize, first collect some clear benchmarks that suggest the size of the URL is seriously impacting the performance of the page. Don't optimize this if it doesn't need it. I've never heard of anyone complaining that a download URL is bad for performance.
You don't need to loop over all users. You should arrange to have the UID of the user available at the time of the resize, so you can update the correct user. You can put the UID in the path of the file upload and parse it out, or you can put the UID in object metadata at the time of the upload.
Either approach is valid, though. Pick the one that suits you the best. Generating it on the backend is probably more resilient to errors.

As Doug Stevenson mentioned in his answer, I also think you are trying to optimize something that's not even a performance or storage problem. However, if you still want to optimize the size of your URL, I have two solutions for you.
As we already know, the URL of a picture looks similar to this:
https://firebasestorage.googleapis.com/v0/b/project-id/o/images_folder%2vvTMvCsCRsckpR3R5Qg2s.jpg?alt=media&token=2277f575-8ff7-2211-8262-a28ef679d703
So the first solution would be to shorten the links using a service like tiny.cc. There are also other examples but I think you get the idea. So in case of the URL above, after you shorten it, will look like this:
http:// tiny.cc /2r4ucz
The second solution requires the saving two things in your database. It is not about shorten the link, it's about storing less data. So as you can see, the URL above is combined from a "BASE_URL":
https://firebasestorage.googleapis.com/v0/b/project-id/o/images_folder
Which includes the name of your project and the name of the folder where you store the images. It also contains the name of the image, which in my case is an id that is generated by Firestore and it's by definion unique. And the last part is the token id:
2277f575-8ff7-2211-8262-a28ef679d703
So the second solution would be to store in your database only the id of the image and the token and then reconstruct the entire URL client side. So in the example above the only things that you should store are:
Firestore-root
|
--- users (collection)
|
--- uid (document)
|
--- id: vvTMvCsCRsckpR3R5Qg2s
|
--- token: 2277f575-8ff7-2211-8262-a28ef679d703
|
--- //Other user details
If your user will always have a single picture then you can use instead of that random id, the uid that is coming from the authentication processs:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();

Related

Understand Dynamic Links Firebase

I would like to understand better Firebase Dynamic Links because i am very new to this subject.
What i would like to know :
FirebaseDynamicLinks.instance.getInitialLink() is supposed to return "only" the last dynamic link created with the "initial" url (before it was shorten) ?
Or why FirebaseDynamicLinks.instance.getInitialLink() doesn't take a String url as a parameter ?
FirebaseDynamicLinks.instance.getDynamicLink(String url) doesn't read custom parameters if the url was shorten, so how can we retrieve custom parameters from a shorten link ?
My use case is quite simple, i am trying to share an object through messages in my application, so i want to save the dynamic link in my database and be able to read it to run a query according to specific parameters.
FirebaseDynamicLinks.instance.getInitialLink() returns the link that opened the app and if the app was not opened by a dynamic link, then it will return null.
Future<PendingDynamicLinkData?> getInitialLink()
Attempts to retrieve the dynamic link which launched the app.
This method always returns a Future. That Future completes to null if
there is no pending dynamic link or any call to this method after the
the first attempt.
https://pub.dev/documentation/firebase_dynamic_links/latest/firebase_dynamic_links/FirebaseDynamicLinks/getInitialLink.html
FirebaseDynamicLinks.instance.getInitialLink() does not accept a string url as parameter because it is just meant to return the link that opened the app.
Looks like there's no straightforward answer to getting the query parameters back from a shortened link. Take a look at this discussion to see if any of the workarounds fit your use case.

Firebase storage url, new file keep same access token

Duplicate of: Firebase storage URL keeps changing with new token
When a user uploads a profile pic I store this in firebase storage with the file name as the uid.
Lets say the user then goes and makes say 100 posts + 500 comments and then updates their profile image.
Currently I have a trigger which goes and updates the profile image url in all of the post and comment documents. The reason I have to do this is that when the image is changed in storage the access token is changed and this is part of the url so the old url no longer works.
What I want to do is not have the access token change. If I can do this I can avoid the mass updates that will massively increase my firestore writes.
Is there any way to do this? or an alternative?
Edit:
Another solution if you don't mind making the file public.
Add this storage rule and you won't have to use a token to access the file.
This will allow read access to "mydir" globally in any subfolder.
match /{path=**}/mydir/{doc} {
allow read: if true;
}
There are only two options here:
You store the profile image URL only once, probably in the user's profile document, and look it up every time it is needed. In return you only have to write it once.
You store the profile image URL for every post, in which case you only have to load the post documents and not the profile URL for each. In return you'll have to write the profile URL in each post document, and update it though.
For smaller networks the former is more common, since you're more likely to see multiple posts from the same user, so you amortizing the cost of the extra lookup over multiple posts.
The bigger the network of users, the more interesting the second approach becomes, as you'll care about read performance and simplicity more than the writes you're focusing on right now.
In the end, there's no singular right answer here though. You'll have to decide for yourself what performance and cost profile you want your app to have.
Answer provided by #Prodigy here: https://stackoverflow.com/a/64129850/10222449
I tried this and it works well.
This will save millions of writes.
var storage = firebase.storage();
var pathReference = storage.ref('users/' + userId + '/avatar.jpg');
pathReference.getDownloadURL().then(function (url) {
$("#large-avatar").attr('src', url);
}).catch(function (error) {
// Handle any errors
});

Updating all the references to an image url in Cloud Firestore when the image is updated using Flutter

In my Flutter app, I have a userData collection on Cloud Firestore where I store user's data including name, image url, etc.. The user can create posts, add comments to post, etc. similar to any other social apps out there and so I have multiple other collections where the user's info is stored including the link to their profile image.
Let's say if the user adds a comment to a post, I save their name, profile image url and comment text as a document inside "postComment" collection and then I display his/her profile image, name and the comment text on the screen by reading this collection and document.
Now, if the user updates their profile image or even their name which will be reflected in the userData collection, I need to make sure that their name and image url are updated in all other collections as well.
What's the easiest and least costly way to do that? Do I need to loop through all my collections and their documents and update the field values, or is there like a simple cloud function that can handle this?
Thanks!
I also store user profile images in Firestore Storage, BUT I use a very consistent schema to make the images easy to "guess":
When I have a document such as "/People/{userID}", and within the document is a field "image" which stores the URL to the image...
...then I store it in Firestore at the path "People/{userID/image/image.jpg" (eg). This way it is trivial to generate a StorageRef to it, and a downloadURL.
All the other uses of it always are to the now-standardized URL. Change the image in Storage; all references update.
For most "user" applications, the only use of the image is to feed it to a web-page, so just the URL is needed, and let the browser do the rest of the work.
As Fattie somewhat more aggressively stated, generally all you need is the URL. But following by itself that means you still would have to find all the references and update them if the user changes the URL. Saving a copy in Firestore Storage, and using that consistent URL, means all references will be "updated" just by changing what is stored at that location. Disadvantage is it will count as a storage read when fetched.
I'm finding duplicating data in NoSQL is great when it's fairly static - created once, and not dynamically changed (which is a LOT of cases). If your application doesn't fit that, it's better to store a reference to the source-of-truth, and incur the cost of the "lookup".
Here's a couple utilities I use to make this easier:
export const makeStorageRefFromRecord = (
record,
key = null,
filename = null
) => {
return FirebaseStorage.ref(
record.ref.path + (key ? "/" + key : "") + (filename ? "/" + filename : "")
);
};
export const makeFileURLFromRecord = (record, key = null, filename = null) => {
return FirebaseStorage.ref(
record.ref.path + (key ? "/" + key : "") + (filename ? "/" + filename : "")
).getDownloadURL();
};
("key" is essentially the fieldname)
remember the refpath is a string of the "/" separated collection/document path to the record, and is completely knowable in a simple situation, such as "People/{userID}". If you keep this internal, you can use "filename" as simple as "image.jpg" so it's always the same - it's unique, because of the path.
Do I need to loop through all my collections and their documents and update the field values
Minimally, yes, that's what you have to do.
or is there like a simple cloud function that can handle this?
You can certainly write your own Cloud Function to do this as well. There is not an existing function that will just do what you want - you have to code it.
Alternatively, you can just store the URL is one document, store the ID of that document in the other documents that need to refer to it, and have the client make an query for the single document with the URL you need.
There are multiple ways to do that.
The best way to do that is instead of storing the profile picture image again and again, you can store document references. If you are storing the images as base64, this would also save a lost of space and is cost efficient.
Another way of doing it is less efficient but you can store the image in firestore and refer it from there.
Both of these are from refereces
The last way of doing it and probably the most inefficient is by querying. You can go to that collection of post (Or if you store each post as a collection, loop through all of them) and then add a where filter and search for the imageURL or more safely a unique ID and then you can change them all one by one
These are the ways that I know

Firebase Storage : Get the token of the URL

I currently have an application that works with Firebase.
I repeatedly load profile pictures. However the link is quite long, it consumes a certain amount of data. To reduce this load, I would like to put the link in raw and only load the token that is added to the link.
To explain, a link looks like this: “https://firebasestorage.googleapis.com/v0/b/fir-development.appspot.com/o/9pGveKDGphYVNTzRE5U3KTpSdpl2?alt=media&token=f408c3be-07d2-4ec2-bad7-acafedf59708”
So I would like to put in gross: https://firebasestorage.googleapis.com/v0/b/fir-developpement.appspot.com/o/
In continuation: “9pGveKDGphYVNTzRE5U3KTpSdpl2” which is the UID of the user that I recover already and the or my problem this poses: “alt = media & token = f408c3be-07d2-4ec2-bad7-acafedf59708” which adds randomly for each photo .
I would like to get back only this last random piece …
Is it possible ?
Thank you
UP : 01/11 Still no solution
It's not supported to break apart and reassemble download URLs. You should be treating these strings as if their implementation details might change without warning.

Firebase query with simple login

I am trying to query firebase where I have used simple login to differentiate between users. As such firebase looks like this:
users/simplelogin:*/favouritecolour
favouritecolour looks like {0: blue, 1: red, 2: green}
I want to be able to query for all users with the same favouritecolour and send their profile information back to be displayed on the web page.
users/simplelogin:*/profileinformation
The problem I have is that simple login uses authData.uid for "simplelogin:*" and as such it is different for each user. I want to look through all users. I am not sure how to reference the path to just the favouritecolour for each user, as when querying I don't want to have to search the entire database. I have looked for a wildcard token to use in the path name. I have also looked at relative path definitions, where one may be able to skip over branches in a path. No luck. What code do I need to write to return profileinformation as a snapshot for all profiles matching favouritecolour red?
Thanks for your help.
You need to organize data according to how they are going to be accessed, not according to how "it makes sense" in traditional relational DB. This also includes data duplication and non-normalization.
Take a look at this.
So, you also need to keep something like this:
/favouritecolor/{color}/ --> { user1, user2, user3 }

Resources