Store files with unique/random names - firebase

With the new Firebase API you can upload files into cloud storage from client code. The examples assume the file name is known or static during upload:
// Create a root reference
var storageRef = firebase.storage().ref();
// Create a reference to 'mountains.jpg'
var mountainsRef = storageRef.child('mountains.jpg');
// Create a reference to 'images/mountains.jpg'
var mountainImagesRef = storageRef.child('images/mountains.jpg');
or
// File or Blob, assume the file is called rivers.jpg
var file = ...
// Upload the file to the path 'images/rivers.jpg'
// We can use the 'name' property on the File API to get our file name
var uploadTask = storageRef.child('images/' + file.name).put(file);
With users uploading their own files, name conflicts are going to be an issue. How can you have Firebase create a filename instead of defining it yourself? Is there something like the push() feature in the database for creating unique storage references?

Firebase Storage Product Manager here:
TL;DR: Use a UUID generator (in Android (UUID) and iOS (NSUUID) they are built in, in JS you can use something like this: Create GUID / UUID in JavaScript?), then append the file extension if you want to preserve it (split the file.name on '.' and get the last segment)
We didn't know which version of unique files developers would want (see below), since there are many, many use cases for this, so we decided to leave the choice up to developers.
images/uuid/image.png // option 1: clean name, under a UUID "folder"
image/uuid.png // option 2: unique name, same extension
images/uuid // option 3: no extension
It seems to me like this would be a reasonable thing to explain in our documentation though, so I'll file a bug internally to document it :)

This is the solution for people using dart
Generate the current date and time stamp using:-
var time = DateTime.now().millisecondsSinceEpoch.toString();
Now upload the file to the firebase storage using:-
await FirebaseStorage.instance.ref('images/$time.png').putFile(yourfile);
You can even get the downloadable url using:-
var url = await FirebaseStorage.instance.ref('images/$time.png').getDownloadURL();

First install uuid - npm i uuid
Then define the file reference like this
import { v4 as uuidv4 } from "uuid";
const fileRef = storageRef.child(
`${uuidv4()}-${Put your file or image name here}`
);
After that, upload with the file with the fileRef
fileRef.put(Your file)

In Android (Kotlin) I solved by combining the user UID with the milliseconds since 1970:
val ref = storage.reference.child("images/${auth.currentUser!!.uid}-${System.currentTimeMillis()}")

code below is combination of file structure in answer from #Mike McDonald , current date time stamp in answer from # Aman Kumar Singh , user uid in answer from #Damien : i think it provides unique id, while making the firebase storage screen more readable.
Reference ref = firebaseStorage
.ref()
.child('videos')
.child(authController.user.uid)
.child(DateTime.now().millisecondsSinceEpoch.toString());

Related

Linking images from Firebase Storage to Firestore document and displaying them in React Native

Background
I'm trying to upload images to firebase storage manually (using the upload file button in the web page), however I have no clue how to later link them to a firestore document. What I have come up with (I'm unsure if it works) is copying the url for the image in the storage bucket and adding it to a string type field in the document called profilePicture. The reason I'm unable to get this to work is that I'm really new to React Native and I don't know how to properly require the images other than typing in the specific local route. Mind you also, the way I'm requiring user data such as a profile name is after logging in with email/password auth I pass the data as a param to react navigation and require it as extraData.
What I have tried
Once I've copied the image url and pasted it in the firestore document I'm doing this:
const profilePicture = props.extraData.profilePicture;
<Image source={require({profilePicture})}/>
I have also tried using backticks but that isn't working either. The error message I'm getting is:
TransformError src\screens\Profile\ProfileScreen.js: src\screens\Profile\ProfileScreen.js:Invalid call at line 27: require({
profilePicture: profilePicture
})
Note: this is an expo managed project.
Question
Is the problem in the code or in the way I'm linking both images? Maybe both? Should I require the document rather than relying on the data passed previously?
Thanks a lot in advance!
Edit 1:
I'm trying to get all info from the current user signed in, after a little research I've come to know about requiring images in this manner:
const ref = firebase.storage().ref('path/to/image.jpg');
const url = await ref.getDownloadURL();
and then I'd require the image as in <Image source={{uri: url}}/>
I get that this could be useful for something static, but I don't get how to update the ref for every single different user.
Edit 2:
Tried using the method mentioned in Edit 1, just to see what would happen, however It doesn't seem to work, the image just does not show up.
Maybe because my component is a function component rather than a class component (?
I understand that your goal is to generate, for each image that is uploaded to Cloud Storage, a Firestore document which contains a download URL.
If this is correct, one way is to use a Cloud Function that is triggered each time a new file is added to Cloud Storage. The following Cloud Function code does exactly that. You may adapt it to your exact requirements.
exports.generateFileURL = functions.storage.object().onFinalize(async object => {
try {
const bucket = admin.storage().bucket(object.bucket);
const file = bucket.file(object.name);
// You can check that the file is an image
const signedURLconfig = { action: 'read', expires: '08-12-2025' }; // Adapt as follows
const signedURLArray = await file.getSignedUrl(signedURLconfig);
const url = signedURLArray[0];
await admin.firestore().collection('profilePictures').add({ fileName: object.name, signedURL: url }) // Adapt the fields list as desired
return null;
} catch (error) {
console.log(error);
return null;
}
});
More info on the getSignedUrl() method of the Admin SDK here.
Also note that you could assign the Firestore document ID yourself, instead of having Firestore generating it as shown in the above code (with the add() method). For example, you can add to the image metadata the uid of the user and, in the Cloud Function,get this value and use this value as the Document ID.
Another possibility is to name the profile image with the user's uid.

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

How to create unique image ID each time upload is pressed using Flutter/Firebase?

I'm trying to make an image upload button and link it to Firebase such that each time the button is pressed, the image is sent to Firebase Storage. Here are the relevant snippets of my code:
// Files, and references
File _imageFile;
StorageReference _reference =
FirebaseStorage.instance.ref().child('myimage.jpg');
Future uploadImage() async {
// upload the image to firebase storage
StorageUploadTask uploadTask = _reference.putFile(_imageFile);
StorageTaskSnapshot taskSnapshot = await uploadTask.onComplete;
// update the uploaded state to true after uploading the image to firebase
setState(() {
_uploaded = true;
});
}
// if no image file exists, then a blank container shows - else, it will upload
//the image upon press
_imageFile == null
? Container()
: RaisedButton(
color: Colors.orange[800],
child: Text("Upload to Firebase Storage"),
onPressed: () {uploadImage();}),
However, each time I press this button, the image overwrites the pre-existing image with the same name, and I was wondering if there's a way to make it so that each time I press the button, the name of the image changes, and as a result the original image isn't overridden. I'd greatly appreciate any help I could get as I'm very new to Flutter and Firebase.
Thank you!
I think you're looking for a UUID generator.
Fortunately there is a packege for that : uuid
String fileID = Uuid().v4(); // Generate uuid and store it.
Now you can use postID as name for your file.
NOTE When you upload your file you may want to generate new uuid to avoid using the old one :)
One more thing: PLEASE dont use DateTime.now() think about if two users uploaded an image at the same time !!
Basically, when you call:
FirebaseStorage.instance.ref().child('myimage.jpg');
You're uploading the file with the same name everytime:
myimage.jpg
In order to fix your problem, you just need to generate a random key for the image. There are a couple ways you could do this:
Ideally, you would use the Uuid package which is specifically for use-cases like this.
If you are set up with Firestore (the database that Firebase offers) then you can push the name of the image to the database, and have it return the DocumentID which Firestore will create a random ID for you that isn't used.
You could also use the current Date/Time (which would be considered a bad practice for major applications, but for a personal project it will serve you just fine):
DateTime.now().toString()
or
DateTime.now().toIso8601String();
Or of course, you can always write your own hashing function, based on the name of the file that you are uploading, which you would get by doing:
_imageFile.toString();
Then once you get the random name of the file, you should upload it like this:
FirebaseStorage.instance.ref().child(myImageName).putFile(_imageFile);
This maybe a late answer but hope it helps someone in future,had the same challange my solution:
I used DateTime.now().toString(),but before that i got the current logged in user UID from firebase,
then added it to every outgoing save to storage request like this
DateTime.now().toString() + (_auth.currentUser!.uid),
This made every file unique and solved the overwriting issue for me.
ARK*
Simply Add a Document Reference
DocumentReference myDoc = FirebaseFirestore.instance
.collection('COLLECTION')
.doc();
myDoc having your new Document Id.
myDoc.set({'data':'test'});

Cosmos DB image/file attachment

I have created a document with pdf attachment using below code and it's working (able to retrieve attached file).
var myDoc = new { id = "42", Name = "Max", City="Aberdeen" }; // this is the
document you are trying to save
var attachmentStream = File.OpenRead("c:/Path/To/File.pdf"); // this is the
document stream you are attaching
var client = await GetClientAsync();
var createUrl = UriFactory.CreateDocumentCollectionUri(DatabaseName,
CollectionName);
Document document = await client.CreateDocumentAsync(createUrl, myDoc);
await client.CreateAttachmentAsync(document.SelfLink, attachmentStream, new
MediaOptions()
{
ContentType = "application/pdf", // your application type
Slug = "78", // this is actually attachment ID
});
I can upload a document directly in blob storage and put that blob URL in the document.
Can anyone help me to understand the value of inbuild attachment feature? how this is better than blob and other option? where cosmos DB keep attachment?
I want to understand which scenario we should consider this option (I know 2GB per account limitation)
Can anyone help me to understand the value of inbuild attachment
feature?how this is better than blob and other option?
Based on this official doc, you could get answer for your question.
You could store two types data:
1.binary blobs/media
2.metadata (for example, location, author etc.) of a media stored in a remote media storage
In addition,attachments has garbage disposal mechanism which is different with Azure Blob Storage I think.
Azure Cosmos DB will ensure to garbage collect the media when all of
the outstanding references are dropped. Azure Cosmos DB automatically
generates the attachment when you upload the new media and populates
the _media to point to the newly added media. If you choose to store
the media in a remote blob store managed by you (for example,
OneDrive, Azure Storage, DropBox, etc.), you can still use attachments
to reference the media. In this case, you will create the attachment
yourself and populate its _media property.
So,per my understanding,if your resource data will be frequently added or deleted, I think you could consider using attachment. You just need to store remote URL into _media property.
where cosmos DB keep attachment?
Attachment is stored in the collection as JSON format document,it can be created, replaced, deleted, read, or enumerated easily using either REST APIs or any of the client SDKs. As I know, it can't display on the portal so far.
BTW, Azure cosmos db is more expensive than blob storage usually.I think cost is an important factor to consider. More details, you could refer to the price doc.
Hope I'm clear on this.

Storing Data in firebase using VBA

Is there any way to upload files using VBA to firebase?
Web code for uploading files in firebase
var storageRef = firebase.storage.ref("folderName/file.jpg");
var fileUpload = document.getElementById("fileUpload");
fileUpload.on(‘change’, function(evt) {
var firstFile = evt.target.file[0]; // get the first file uploaded
var uploadTask = storageRef.put(firstFile);
uploadTask.on(‘state_changed’, function progress(snapshot) {
console.log(snapshot.totalBytesTransferred); // progress of upload
});
});
But how do I use this in VBA? any help is appreciated. Also if you can point to me in right direction.
I did some research on Firebase. Currently you cannot use VBA to upload files directly to FB Storage, through API.
You can try using JS, as mentioned by you.
Another alternative will be Google Cloud Storage. Which is the back-end for firebase storage.
two ways of doing this.. Assuming there is a website where you can upload files, in such case you can use the standard IE object to navigate to a specific website, find the controls, select the files and using submit or click function upload the file..
The other way is to write a web-service and call it from vba. Both suggestions involves in calling the website/service.
Dim IE As Object
Set IE = CreateObject("internetexplorer.application")
IE.Navigate "Your upload page.."
Do While IE.ReadyState <> 4 Or IE.Busy = True
DoEvents
Loop
we don't have enough information so I just continue with the logic.
Find the upload document/filename field. insert your filename
IE.Document.getElementById("txt_upload_field").Value = myFileName
find the button/element to upload and perform click/submit action.
wait for your website and read the result
or you can write your own web service and perform a web-request using XMLHTTP. I personally would go for web-service.
there is also a third way. FTPing to your server..!

Resources