Uploading Image to Firebase Storage and Database

How to properly upload image to Firebase Storage and save link to firestore

You need to wait until the file has been completely uploaded before you can request its download URL.

storageRef.put(file).then(() => {
firebase.storage().ref("users").child(user.uid).getDownloadURL()
.then((downloadURL) => {
...

Upload Images to Firebase Storage and store a link in Firebase Database

You have to call the listener to get the download uri from firebase storage. Use following code to do that. Read the docs

filereference.putFile(imageuri)
.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Toast.makeText(Upload_Photos.this, "Upload Successfully", Toast.LENGTH_SHORT).show();
filepath.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
Uri downloadUrl = uri;
Upload upload = new Upload(name.getText().toString().trim(),downloadUrl.toString());
progressDialog.show();
String uploadId = mDatabaseref.push().getKey();
mDatabaseref.child(uploadId).setValue(upload);
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.dismiss();
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {

}
})
.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
@Override
public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
double progress = (100.0*taskSnapshot.getBytesTransferred())/taskSnapshot.getTotalByteCount();
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.setMessage("Uploaded " +(int)progress+"%");



}
});

When uploading a image for one user to firebase storage, the link of the image updates for every user in database

According to your last comment:

Yes, Firebase email Authentication

Then you should consider saving user data, under the UID that comes from the authentication process and not under a pushed ID, as I see in your screenshot now. This means that you should add a User object to the database using:

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference usersRef = rootRef.child("users");
usersRef.child(uid).setValue(userObj).addOnCompleteListener(/* ... /*);

And not using:

usersRef.push().setValue(userObj).addOnCompleteListener(/* ... /*);

That beeing said, to be able to update the image that is uploaded to the node of the logged-in user, then please use the following updated method:

final StorageReference strRef = storageReference.child("users/"+ email_i +"/profile.jpg");
strRef.putFile(image_uri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
Toast.makeText(getApplicationContext(), "Image has been uploaded", Toast.LENGTH_LONG).show();
strRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {
Picasso.get().load(uri).fit().into(profPic);
imgRef = uri.toString();

String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference usersRef = rootRef.child("users");
Map<String, Object> updates = new HashMap<String, Object>();
updates.put("profile_url", imgRef);
usersRef.child(uid).updateChildren(updates).addOnSuccessListener(/* ... /*);
}
});
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull /*@org.jetbrains.annotations.NotNull*/ Exception e) {
Toast.makeText(getApplicationContext(), "Failed to update profile picture", Toast.LENGTH_LONG).show();
}
});

See, there is no for-loop involed? That was causing you trouble, as it was updating all child under "users" node.

Uploading Image to Firebase Storage and Database

Organize your upload and save funcs like this:

func uploadMedia(completion: @escaping (_ url: String?) -> Void) { 
let storageRef = FIRStorage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.myImageView.image!) {
storageRef.put(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
completion((metadata?.downloadURL()?.absoluteString)!))
// your uploaded photo url.
}
}
}

Next just connect to FIRDatabase and save it to your node.

 @IBAction func addPost(_ sender: Any) {
if self.titleText.text != "" && self.authorText.text != ""
&& self.mainText.text != "" && self.dateText.text != "" {

uploadMedia() { url in
guard let url = url else { return }
ref?.child("Posts").childByAutoId().setValue([
"Title" : titleText.text,
"Article" : mainText.text,
"Author" : authorText.text,
"Date" : dateText.text,
"myImageURL" : url
])
}
}

You can also look at my answer about uploading data and saving URL's to database

Upload in Firebase Storage and fetch link to add it in Realtime Database

Issue

This appears to be a state mutation in the uploadImages callback. A reference to the imagesUriArray state is cached locally, mutated (i.e. direct push into array), and then the same reference is saved back into state. This doesn't trigger react to rerender with any updated state value.

const uploadImages = () => {
...

pictures.forEach( async (obj) => {
const id = obj.id
const uri = obj.uri

const response = await fetch(uri);
const blob = await response.blob();
var ref = storageUpload(storage, path)
await uploadBytes(ref, blob, metadata)

await getDownloadURL(ref)
.then((metadata) => {
let array = imagesUriArray // <-- reference to state

let object = {
"id": id,
"uri": metadata
}

array.push(object) // <-- state mutation
setImageUriArray(array) // <-- same state reference
})
.catch((error) => {
console.log(error)
});
})
};

Solution

Use a functional state update to update from the previous state. Create a shallow copy of the previous state and append the new data.

const uploadImages = () => {
...

pictures.forEach( async (obj) => {
const { id, uri } = obj;

const response = await fetch(uri);
const blob = await response.blob();
const ref = storageUpload(storage, path);
await uploadBytes(ref, blob, metadata);

await getDownloadURL(ref)
.then((uri) => {
setImageUriArray(imagesUriArray => [
... imagesUriArray, // <-- shallow copy
{ id, uri }, // <-- append new object
]);
})
.catch(console.log);
})
};

Update

uploadImages needs to return a Promise so that addItem can wait for it to complete its asynchronous code. addItem also needs to access the updated imagesUriArray that uploadImages updates.

Map the pictures array to an array of Promises (i.e. an async fetchImageUri function) that eventually returns the object with id and new uri properties.

const uploadImages = () => {
...

const fetchImageUri = async ({ id, uri }) => {
try {
const response = await fetch(uri);
const blob = await response.blob();
const ref = storageUpload(storage, path);
await uploadBytes(ref, blob, metadata);

const newUri = await getDownloadURL(ref);
return { id, uri: newUrl };
} catch(error) {
console.log(error);
}
}

return Promise.all(pictures.map(fetchImageUri));
};

Update addItem to wait for the resolved array of Promises that contain the uploaded image data. Enqueue the imagesUriArray state update here, then continue the rest of the function referencing the returned uploadedImages array from uploadImages.

const addItem = async () => {
const uploadedImages = await uploadImages();
setImageUriArray(imagesUriArray => [
...imagesUriArray, // <-- shallow copy
...uploadedImages, // <-- append new objects
]);

const changes = ref(db, path);
get(changes).then(async (snapshot) => {
if (snapshot.val().data !== undefined) {
const fetchedArray = snapshot.val().data;
const object = {
id: `${Math.random()}`,
images: uploadedImages,
};

update(changes, { data: [...fetchedArray, object] });
}
});
}


Related Topics



Leave a reply



Submit