I used react-native-firebase lib in my RN App
and I use Firebase Storage to save image in the cloud so it's work very well but
In profile screen and I have an Edit function that replaces the old image with a new one but it's just adding a new image to Firebase Cloud Storage not replaced it
so there any way for just replacing old with new?
because I don't want to increase my storage when users change his image profile
here is my function that's save new image profile
EditImg = async () => {
const {uid, avatar} = this.state;
let providerRef = database().ref(`Providers/users/${uid}`);
const path = 'Avatar_' + new Date().getTime() + '.jpg';
const ref = storage().ref(`providers/${uid}/providerGalary/${path}`);
await ref.putFile(avatar).then(() => {
ref.getDownloadURL().then(
img => {
console.log('img', img);
providerRef
.update({
avatar: img,
})
.then(() => console.log('saved to Db Successfully'));
},
error => console.log(error),
);
});
};
Change this:
const path = 'Avatar_' + new Date().getTime() + '.jpg';
const ref = storage().ref(`providers/${uid}/providerGalary/${path}`);
await ref.putFile(avatar).then(() => {
Into this:
const path = 'Avatar_' + uid + '.jpg';
const ref = storage().ref(`providers/${uid}/providerGalary/${path}`);
await ref.putFile(avatar).then(() => {
Use the uid instead of new Date, since the uid will stay the same thus you will be able to replace the image instead of adding a new one
Related
I am creating an app using firebase and react.js. When I reload the app, only the very last image shows up, when instead all of them should be loaded on the first page. Here is my handleUpload function where the issue lies (Updated):
const handleUpload = () => {
//const storageRef = ref(storage, `images/${image.name}`);
const randomId = doc(collection(db, "temp")).id;
const storageRef = ref(storage, `images/${randomId}`);
const uploadTask = uploadBytesResumable(storageRef, image);
uploadTask.on(
"state_changed",
(snapshot) => {
//Progress function ... (shows the load bar)
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
setProgress(progress);
},
(error) => {
//Error Function...
console.log(error);
alert(error.message);
},
async () => {
//complete function
const url = await getDownloadURL(storageRef);
const docRef = await addDoc(collection(db, "posts"), {
imageURL: url,
caption: caption,
username: username,
timestamp: serverTimestamp()
});
console.log("THE APP HAS POSTED");
setProgress(0);
setCaption('');
setImage(null);
setUrl('');
}
)
}
Here is my storage console after restarting and posting 2 photos from an iPhone:
There should be 2 photos there, but only the latest one shows up, with the name ${image.name}, which I don't understand because I have that line commented out.
Note: this issue only occurs when someone uploads on a phone since there are no file names on a phone, just a picture from a camera roll. Is there a way to fix this that anyone can think of?
Firebase storage doesn't generate a random ID for new files. You must specify then file name while uploading it. You can use a package like UUID or even Firestore to generate random IDs as shown below:
const randomId = doc(collection(db, "temp")).id
const storageRef = ref(storage, `images/${randomId}`);
Also make sure you add the file extension at the end of filename.
Are you trying to upload image with same name? In that case it'll overwrite the existing image with that name.
I want to save images in Firebase Storage with its own name.....
so, this is my uploading code..
storage.ref('users/'+ imgId + "/post.jpg").put(file).then(function () {
console.log("uploaded");
and this is my retrieval code..
storage.ref('users/'+ user.uid + "/post.jpg").getDownloadURL().then(img => {
postImage = img
}).catch(error => {
console.log(error);
})
Whats happening is that when ever I upload a new image it overrides the older image and also overrides the image in UI. can anyone has a solution ?
By using storage.ref('users/'+ user.uid + "/post.jpg").put(file) you are uploading the file as users/USER_ID/post.jpg.
If the intention is to upload a "primary" image for a particular post, instead save the image as posts/POST_ID/main.jpg (hard to trace author) or users/USER_ID/images/POST_ID-main.jpg (easy to trace author).
// for "users/USER_ID/images/POST_ID-main.jpg"
const postImageRef = storage
.ref('users/'+ user.uid + "/images/" + postId + "-main.jpg");
// OR
// for "users/USER_ID/images/POST_ID-main.jpg"
const postImageRef = storage
.ref('users/'+ user.uid)
.child("images")
.child(postId + "-main.jpg");
// OR
// for "posts/POST_ID/main.jpg"
const postImageRef = storage
.ref('posts/'+ postId)
.child(main.jpg");
postImageRef.put(file)
.then(() => {
console.log("uploaded");
return postImageRef.getDownloadURL();
})
.then((imageURL) => {
// TODO: use imageURL
})
As you seem to be setting a variable postImage from inside the promise, you are going to run into race conditions. To avoid that, switch to async/await syntax like so:
// elsewhere
const POSTS_REF = database.ref('posts');
const newPostRef = POSTS_REF.push();
const postId = newPostRef.key;
const postImageStorageRef = storage
.ref('users/'+ user.uid)
.child("images")
.child(postId + "-main.jpg");
await postImageStorageRef.put(file);
console.log("uploaded");
const postImage = await postImageStorageRef.getDownloadURL();
// do something with postImage
await newPostRef
.set({
image: postImage,
uid: user.uid,
...
});
console.log("Created post #" + postId);
I try to upload a "base64" image to Firebase by using React Native on iOS. But when I try to upload the image, I get following error:
undefined is not an object (evaluating 'uri.substring')
I get my image by using route.params and if I display the image in a view like this, the image is displayed.
<Image style={styles.image} source={{ uri: `data:image/png;base64,${myImage}` }}/>
Should I do anything else if the image is in "base64" or what else should I do?
This is my code:
// Here is how I get the image
const { myImage } = props.route.params;
const uploadImage = async () => {
const {uri} = myImage;
const filename = uri.substring(uri.lastIndexOf('/') + 1);
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
setUploading(true);
setTransferred(0);
const task = storage()
.ref(filename)
.putFile(uploadUri);
// set progress state
task.on('state_changed', snapshot => {
setTransferred(
Math.round(snapshot.bytesTransferred / snapshot.totalBytes) * 10000
);
});
try {
await task;
} catch (e) {
console.error(e);
}
setUploading(false);
Alert.alert(
'Photo uploaded!',
'Your photo has been uploaded to Firebase Cloud Storage!'
);
};
Looking at your current code, you take myImage out of props.route.params, and this is a string of Base 64 characters corresponding to a PNG image (such as iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAAAASUVORK5CYII= - the data of a PNG for a single #FFFFFF pixel).
const { myImage } = props.route.params;
Below that you try to get a property uri out of this myImage string. As this property doesn't exist, you will get undefined. This uri = undefined value then throws an error on the next line.
const { uri } = myImage; // uri is undefined! ("".uri === undefined)
const filename = uri.substring(uri.lastIndexOf('/') + 1); // throws error!
The correct way to upload a Data URL would be to use the Reference#putString() method as covered in the documentation here.
Applying this to your code, you would use:
const { myImage } = props.route.params;
const uploadImage = async () => {
const dataUrl = `data:image/png;base64,${myImage}`;
// you could use a Firestore Doc ID, a RTDB Push ID or
// some `uuid` implementation to generate a suitable filename.
const storageRef = storage()
.ref(/* provide a path for the image */);
const uploadTask = storageRef
.putString(dataUrl, 'data_url');
uploadTask.on('state_changed', snapshot => {
setTransferred(
Math.round(snapshot.bytesTransferred / snapshot.totalBytes) * 10000
);
});
try {
await uploadTask;
setUploading(false);
Alert.alert(
'Photo uploaded!',
'Your photo has been uploaded to Firebase Cloud Storage!'
);
} catch (err) {
// TODO: Check value of `err.code` and handle appropriately.
console.error('Upload failed: ', err);
Alert.alert(
'Photo upload failed!',
'Your photo didn\'t upload properly!'
);
}
}
To prevent overwriting someone else's data and make security rules easier to implement, you should prefix the uploaded file with the user's ID similar to:
const storageRef = storage()
.ref('userUploads')
.child(firebase.auth().currentUser.uid)
.child(/* generated image ID */);
// builds a reference to /userUploads/someUserId/someImageId
If your image data string looks something like this:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABQAAAALQCAYAAADPfd1WAAAAAXNSR0IArs4c6QAAIABJREFUeF7svXmPLEly4GcRkVnnO/r1656+5uRcJEGAnMEO ...
Then you can use the putString method - but you need to remove the starting info data:image/png;base64, first.
await storage.ref("pictures/thumbnail.png").putString(thumbnailImageData.split("data:image/png;base64,")[1], "base64");
I am uploading an image to firebase storage and retrieving its downloadURL. I have created a ADS node in firebase database and a newAd object in its node.
What i want is that the downloadURL of that image is saved in newAd.picLink i.e property of newAd(Object).
addSubmitted.addEventListener("click", e => {
const newAds = _db.ref("ADS").push();
const newAd = {};
const ref = firebase.storage().ref();
const file = $("#exampleInputFile").get(0).files[0];
const name = +new Date() + "-" + file.name;
const task = ref.child(name).put(file, { contentType: file.type });
task.snapshot.ref.getDownloadURL().then(downloadURL => {
console.log("File available at", downloadURL);
newAd.picLink = downloadURL; /*this isn't working how can i set
downloadURL as newAd objects property*/
});
});
You're not writing anything to the database after the file has been uploaded. The simplest fix is to update newAds in there:
task.snapshot.ref.getDownloadURL().then(downloadURL => {
console.log("File available at", downloadURL);
newAdS.update({ picLink: downloadURL });
});
I'm uploading a camera picture to a Firebase Storage bucket. When its done, i retrieve the image-url to display it in my view. The current Coding looks like this:
takePicture() {
this.camera.getPicture(this.cameraOptions)
.then(data => {
let base64Image = 'data:image/jpeg;base64,' + data;
let storageRef = firebase.storage().ref();
let imageName = this.imageSrv.generateUUID();
let imageRef = storageRef.child(`${this.afAuth.auth.currentUser.uid}/${imageName}.jpg`);
imageRef.putString(base64Image, 'data_url').then(data => {
imageRef.getDownloadURL().then(data => { this.url = data.toString(); })
}
)
});
}
It works, but it is not really great. I would like to put the coding to handle the database into a provider. How can this be achieved when using promises on the firebase storage? I cant directly access and return the URL in the providers methods because of the async handling:
getURL( userID: string, imageName: string) {
let storageRef = firebase.storage().ref();
let imageRef = storageRef.child(`${userId}/${imageId}.jpg`);
return imageRef.getDownloadURL();
}
I would also like to do this for N images to view an image gallery.
Thank you in advance.