How to remove images from Firebase Storage? - javascript

I'm trying to adapt a React Native project to the new Firebase methods. In it I upload images to Storage and they are added to the App interface. I can also remove these images from the interface as shown in the following code:
const removeImage = (img) => { // delete an image selected by the user
Alert.alert(
"Eliminar imagen",
"¿Estás seguro de eliminar esta imagen?",
[
{
text: "Cancelar",
style: "cancel",
},
{
text: "Eliminar",
onPress: () => {
const result = filter(
formik.values.images,
(image) => image !== img
)
formik.setFieldValue("images", result)
},
},
],
{ cancelable: false }
)
}
The problem is that in this way, they are only removed from my App, while the images are still stored in Firebase. My idea is that when I remove the images from the frontend, they will also be removed from the Firebase Storage.
I have read Firebase documentation, and this would be possible with the deleteObject function
const storage = getStorage();
// Create a reference to the file to delete
const desertRef = ref(storage, 'images/desert.jpg');
// Delete the file
deleteObject(desertRef).then(() => {
// File deleted successfully
}).catch((error) => {
// Uh-oh, an error occurred!
})
I did some test, and I can't get it to work.
I don't know exactly how I should add the Firebase instructions shown here.
How should I implement this function in my code to remove images from Storage?
Thank you
import { getStorage, ref, deleteObject, uploadBytes, getDownloadURL } from "firebase/storage"
export function UploadImagesForm(props) {
const { formik } = props
const [isLoading, setIsLoading] = useState(false) // status for loading
// Function in charge of opening the image gallery
const openGallery = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
})
if (!result.cancelled) {
// console.log('buscando imagenes')
setIsLoading(true) // uploading the image
uploadImage(result.uri)
}
}
// function to upload the images to Firebase
const uploadImage = async (uri) => {
const response = await fetch(uri)
const blob = await response.blob()
const storage = getStorage()
const storageRef = ref(storage, `restaurants/${uuid()}`)
// we go to storage where we want to save the images
uploadBytes(storageRef, blob).then((snapshot) => {
// console.log(snapshot)
updatePhotosRestaurant(snapshot.metadata.fullPath)
})
}
// we take the URL in the previous function and set it in the state of the form
const updatePhotosRestaurant = async (imagePath) => {
const storage = getStorage()
const imageRef = ref(storage, imagePath)
const imageUrl = await getDownloadURL(imageRef) // get the url
// code to upload all images without replacing them
// get the current images and add the new ones with the array
formik.setFieldValue("images", [...formik.values.images, imageUrl])
setIsLoading(false)
}
const removeImage = (img) => { // delete an image selected by the user
Alert.alert(
"Eliminar imagen",
"¿Estás seguro de eliminar esta imagen?",
[
{
text: "Cancelar",
style: "cancel",
},
{
text: "Eliminar",
onPress: () => {
const result = filter(
formik.values.images,
(image) => image !== img
)
formik.setFieldValue("images", result)
},
},
],
{ cancelable: false }
)
}
return (
<>
<ScrollView
style={Styles.viewImage}
horizontal
showsHorizontalScrollIndicator={false}
>
<Icon
type="material-community"
name="camera"
color="#a7a7a7"
containerStyle={Styles.containerIcon}
onPress={openGallery}
/>
{map(formik.values.images, (image) => ( // display the images on the screen
<Avatar
key={image}
source={{ uri: image }}
containerStyle={Styles.imageStyle}
onPress={() => removeImage(image)}
/>
))}
</ScrollView>
<Text style={Styles.error}>{formik.errors.images}</Text>
<LoadingModal show={isLoading} text="Subiendo la imagen" />
</>
)
}

I finally figured out where to implement the deleteObject function in my file to make it all work.
You can delete the images from the Application and Firebase Storage at the same time.
I found a React expert who helped me with this.
As the Firebase documentation says:
To delete a file, first create a reference to that file.
( const imageRef = ref(storage, img ))
Firebase explains it like this:
import { getStorage, ref, deleteObject } from "firebase/storage";
const storage = getStorage();
// Create a reference to the file to delete
const desertRef = ref(storage, 'images/desert.jpg');
Then call the delete() method, (in my case: deleteObject(imageRef) ), for that reference, which will return either a Promise that resolves, or an error if the Promise is rejected.
import { getStorage, ref, deleteObject } from "firebase/storage";
const storage = getStorage();
// Create a reference to the file to delete
const desertRef = ref(storage, 'images/desert.jpg');
// Delete the file
deleteObject(desertRef).then(() => {
// File deleted successfully
}).catch((error) => {
// Uh-oh, an error occurred!
});
I just hope this can help other users who are in my situation learning Firebase
I show. the complete file so that they do not have the doubts that I had, which was the correct place where I should place the Firebase methods
const storage = getStorage()
const imageRef = ref(storage, img)
deleteObject(imageRef).then(() => { // also remove the image from Firebase
console.log("la imagen se elimino");
}).catch((error) => {
console.log("ocurrio un error: ", error)
})
Thanks to #FrankvanPuffelen and #BhavyaKoshiya who tried to help.
import { getStorage, ref, uploadBytes, getDownloadURL, deleteObject } from 'firebase/storage'
import { v4 as uuid } from 'uuid'
import { map, filter } from 'lodash'
import { LoadingModal } from '../../Shared/LoadingModal/LoadingModal'
import Styles from './Styles'
export function UploadImagesForm(props) {
const { formik } = props
const [isLoading, setIsLoading] = useState(false) // status for loading
// Function in charge of opening the image gallery
const openGallery = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
})
if (!result.cancelled) {
// console.log('buscando imagenes')
setIsLoading(true) // uploading the image
uploadImage(result.uri)
}
}
// function to upload the images to Firebase
const uploadImage = async (uri) => {
const response = await fetch(uri)
const blob = await response.blob()
const storage = getStorage()
const storageRef = ref(storage, `restaurants/${uuid()}`)
// we go to storage where we want to save the images
uploadBytes(storageRef, blob).then((snapshot) => {
// console.log(snapshot)
updatePhotosRestaurant(snapshot.metadata.fullPath)
})
}
// we take the URL in the previous function and set it in the state of the form
const updatePhotosRestaurant = async (imagePath) => {
const storage = getStorage()
const imageRef = ref(storage, imagePath)
const imageUrl = await getDownloadURL(imageRef) // get the url
// code to upload all images without replacing them
// get the current images and add the new ones with the array
formik.setFieldValue("images", [...formik.values.images, imageUrl])
setIsLoading(false)
}
const removeImage = (img) => { // delete an image selected by the user
Alert.alert(
"Eliminar imagen",
"¿Estás seguro de eliminar esta imagen?",
[
{
text: "Cancelar",
style: "cancel",
},
{
text: "Eliminar",
onPress: async () => {
const result = filter(
formik.values.images,
(image) => image !== img
)
formik.setFieldValue("images", result)
**// THIS IS THE CODE I ADDED FROM FIREBASE**
const storage = getStorage()
const imageRef = ref(storage, img)
deleteObject(imageRef).then(() => { // also remove the image from Firebase
console.log("la imagen se elimino");
}).catch((error) => {
console.log("ocurrio un error: ", error)
})
**// END OF THE CODE I ADDED FROM FIREBASE**
},
},
],
{ cancelable: false }
)
}

Great explanation! If anyone has the ref URLs stored in state already, I added the below to my useImages hook and it worked great.
const storage = getStorage();
const deleteImg = (refUrl) => {
const imageRef = ref(storage, refUrl)
deleteObject(imageRef)
.catch((error) => {
console.log("Failed to delete image: ", error)
})
}

You can try this
let imageRef = storage.refFromURL(URL);
imageRef.delete()

Related

How to upload a blob into firebase Storage?

i'm using next js 13 with firebase v9. and i'm using a drop zone to upload images. the dropzone returns an array with blob as it's src.
[
{
id: 1
name: "image_processing20220628-4591-yzir35.png"
src: "blob:http://localhost:3000/6e2f33e5-a749-4e9a-b502-d20b8e3f38ca"
}
...
]
the above array is returned from the drop zone. and when i tried to upload to firebase storage it throws an error .
FirebaseError: Firebase Storage: Object 'livingImages/blob:http:/localhost:3000/ca0e3eaf-dbe9-4d77-8053-f4b6d1bd8600' does not exist. (storage/object-not-found)
so how can i upload blob of images to firebase storage?
const imgURL = [];
//this is the images stored inside Redux
const images = useSelector(selectImages);
const storage = getStorage();
images.map(async (file) => {
const storageRef = ref(storage, `livingImages/${file.src}`);
await getDownloadURL(storageRef).then((url) => {
imgURL.push(url);
});
});
const createDocument = () => {
const docRef = doc(db, "livingPosts", session?.user?.email);
const colRef = collection(docRef, "posts");
addDoc(colRef, {
name: "test upload",
images: imgURL,
});
};
the dropzone code
const dispatch = useDispatch();
const images = useSelector(selectImages);
const [files, setFiles] = useState(images == [] ? [] : images);
const {getRootProps, getInputProps} = useDropzone({
onDrop: (acceptedFiles) => {
acceptedFiles.map((file, index) => {
const reader = new FileReader();
reader.onload = async function (e) {
const options = {
maxSizeMB: 5,
maxWidthOrHeight: 1920,
useWebWorker: true,
};
const compressedFile = await imageCompression(file, options);
const tot = parseInt(acceptedFiles.length) + parseInt(files.length);
if (tot > 9) {
alert("select maximum of 9 images");
} else if (parseInt(acceptedFiles.length) > 9) {
alert("maximum images to be selected is 9");
} else if (parseInt(files.length) < 9) {
setFiles((prevState) => [
...prevState,
{
id: index,
src: URL.createObjectURL(compressedFile),
name: file.name,
},
]);
files.map((filename) => {
acceptedFiles.forEach((newFile) => {
if (newFile.name == filename.name) {
alert("a duplicate image is detected");
setFiles(
files,
files.filter((val) => val !== newFile)
);
}
});
});
} else {
alert("something went wrong");
}
};
reader.readAsDataURL(file);
return file;
});
},
})
and the output of the dropzone is
As mentioned in the comments, you'll need the actual File or Blob object to upload the file and not the object URL. You can set the blob in state as shown below:
setFiles((prevState) => [
...prevState,
{
id: index,
src: URL.createObjectURL(compressedFile),
blob: compressedFile, // <-- add blob
name: file.name,
},
]);
Then to upload the files and storing download URLs in Firestore document, try the following function:
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { addDoc } from "firebase/firestore";
const uploadFiles = async () => {
console.log(files);
const promises = files.map((file) => {
const storageRef = ref(storage, `images/${file.name}`);
return uploadBytes(storageRef, file.blob);
});
// upload all files
const res = await Promise.all(promises);
// get download URLs
const links = await Promise.all(res.map((r) => getDownloadURL(r.ref)));
console.log({ links })
// Add Firestore document
const colRef = collection(db, "livingPosts", session?.user?.email, "posts")
const docRef = await addDoc(colRef, {
name: "test",
images: links,
});
console.log("Document written with ID: ", docRef.id);
};
You can call this function on a submit button click or any event when you want to start the upload.

Adding new user information from Firebase v8 to v9

I have a code to create user with image and username to Firebase v8 and I can't replace it to Firebase v9.
Can someone help me? Thank you!
import { useState, useEffect } from 'react'
import { projectAuth, projectStorage, projectFirestore } from '../firebase/config'
import { useAuthContext } from './useAuthContext'
export const useSignup = () => {
const [isCancelled, setIsCancelled] = useState(false)
const [error, setError] = useState(null)
const [isPending, setIsPending] = useState(f`enter code here`alse)
const { dispatch } = useAuthContext()
// upload user thumbnail
const uploadPath = `thumbnails/${res.user.uid}/${thumbnail.name}`
const img = await projectStorage.ref(uploadPath).put(thumbnail)
const imgUrl = await img.ref.getDownloadURL()
// add display AND PHOTO_URL name to user
await res.user.updateProfile({ displayName, photoURL: imgUrl })
import {st, db} from '../firebase/config'
import { getDownloadURL, ref, uploadBytesResumable } from 'firebase/storage';
import { doc, setDoc } from 'firebase/firestore';
const _storageRef = ref(st, "thumbnails/" + res.user.uid + "/" + thumbnail.name)
const _uploadTask = uploadBytesResumable(_storageRef, file);
_uploadTask.on("state_changed", (snapshot) => {
console.log((snapshot.bytesTransferred/snapshot.totalBytes) * 100)
},(error) => {
console.error(error)
}, async () => {
await getDownloadURL(_storageRef)
.then((url) => {
//update database with the new image url
console.log(url)
//setDoc(doc(db, "users", uid), {
// photoURL: url
// }, {merge: true}) <- to not erase the rest of the values like displayName
})
.catch(error => console.error(error))
})
The documentation has examples of both V8 (namespaced) and V9 (Modular) syntax. You can just switch to modular tab for reference. For this case, try refactoring the code as shown below:
import { ref, uploadBytes } from "firebase/storage"
import { updateProfile } from "firebase/auth"
const storageRef = ref(projectStorage, `thumbnails/${res.user.uid}/${thumbnail.name}`);
// 'file' comes from the Blob or File API
// uploadBytes() instead of .put()
uploadBytes(storageRef, file).then(async (snapshot) => {
console.log('Uploaded a blob or file!');
// updateProfile() is now a top-level function
// and not a method on User object
await updateProfile(res.user, {
displayName: name
});
console.log("User profile updated")
});
Also do ensure that you've initialized auth and storage using getAuth() and getStorage() respectively.
Checkout:
Upload files with Cloud Storage on Web
Update user profile using Firebase Modular SDK

How to get image filename from Firebase Storage?

I am using the following:
const [allImages, setImages] = useState([]);
const getFromFirebase = () => {
//1.
let storageRef = storage.ref(user1);
//2.
storageRef.listAll().then(function (res) {
//3.
res.items.forEach((imageRef) => {
console.log(imageRef);
imageRef.getDownloadURL().then((url) => {
//4.
setImages((allImages) => [...allImages, url]);
});
});
})
.catch(function (error) {
console.log(error);
});
console.log(allImages);
};
and then displaying via:
<button onClick={getFromFirebase}>Show</button><br/><br/>
<div id="photos">
{allImages.map((image) => {
return (
<div key={image} className="image">
<img className="uploadedfile" src={image} alt="" />
<button className="buttondelete" onClick={() => deleteFromFirebase(image)}>
Delete
</button>
</div>
I realise this is returning the getDownloadURL for the image URL, but how do I also return the image filename?
To get the filename you can use getMetadata:
imageRef.getMetadata().then(metadata => {
// do something with metadata.name
});
You can use the 'getMetaData' method.
import { getStorage, ref, getMetadata } from "firebase/storage";
// Create a reference to the file whose metadata we want to retrieve
const storage = getStorage();
const forestRef = ref(storage, 'images/forest.jpg');
// Get metadata properties
getMetadata(forestRef)
.then((metadata) => {
// Metadata now contains the metadata for 'images/forest.jpg'
})
.catch((error) => {
// Uh-oh, an error occurred!
});
You can refer Firebase Docs - File Metadata for a better understanding
To get the file name and url or download link of the videos or files from the firebase storage you can use these two functions in react and javascript
// function to fetch videos/files from firebase storage
const fetchVideos = async () => {
const storageRef = firebase.storage().ref("storage folder name");
const videos = [];
await storageRef
.listAll()
.then(async function (result) {
result.items.forEach(async function (videoRef) {
// getting the name of the file
const videoName = videoRef.name;
//getting the url of the file -> calling another function for this
const videoUrl = await getVideoUrl(videoRef);
// creating the object with name and url
const videoObj = {
videoName,
videoUrl,
};
console.log("video obj", videoObj);
videos.push(videoObj);
});
})
.catch(function (error) {
// Handle any errors
return [];
});
}
// function to get download url
const getVideoUrl = (imageRef) => {
const videoLink = imageRef
.getDownloadURL()
.then(function (videoUrl) {
// console.log("videoUrl", videoUrl);
return videoUrl;
})
.catch(function (error) {
// Handle any errors
return "";
});
return videoLink;
};

How to upload Multiple Data to Firebase Store without duplicate

I've googled about how to send and get the Multiple data on firebase.
but none of them worked for me. and some of them work strangely like my code.
My Code uploads multiple data. but it is uploaded in duplicate.
What I wanted to do is uploading different data separately.
Please let me know what I have to modify.
Here is My code.
const [file, setFile] = useState([]);
function handleChange(e) {
for (let i = 0; i < e.target.files.length; i++) {
const newFile = e.target.files[i];
setFile(newFile);
}
}
function uploadImageAsPromise() {
Promise.all(
[file].map((file) => {
return new Promise(() => {
const uploadTask = storage.ref(`files/${file.name}`).put(file);
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => {
console.log(snapshot);
storage
.ref("files")
.child(file.name)
.getDownloadURL()
.then((url) => {
//post image inside the db
const itemObj = {
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
caption: file.name,
fileUrl: url,
size: snapshot._delegate.bytesTransferred,
};
db.collection("myFiles").add(itemObj);
setUploading(false);
setOpen(false);
setFile(null);
});
})
}),
storage
.ref("files")
.child(file.name)
.getMetadata()
.then((meta) => {
console.log(meta.size);
})
}))
}
<input type="file" onChange={handleChange} multiple />
<button onClick={uploadImageAsPromise}>Upload</button>

upload multiple files to firebase storage issues

i am working on a cooking android app where firebase is the backend, i need to upload multiple images of a recipe in firebase stoarge and then store the downloadurl into firebase database.
i managed to upload the files into firebase but i am having some trouble to get the downloadUrl of these files.
i have created an array of promises to upload the files and i had created another array to store the url of each file which i get when it finishes the uploading task.
here is my code
var promises = [];
for (var i=0 ;i< prepaImages.length;i++)
{
//alert(prepaImages[i].name);
var storageRef = firebase.storage().ref("receipes"+"/"+category+"/"+title+"/"+uploadTime+prepaImages[i].name );
var uploadTask = storageRef.put(prepaImages[i]);
promises.push(uploadTask);
uploadTask.on('state_changed', snapshot => {
var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
$("#prepaImageUploadprogress").html(Math.round(percentage)+"%");
$("#prepaImageUploadprogress").attr("style", "width:"+percentage+"%");
}, error => { alert(error) }, () => {
uploadTask.snapshot.ref.getDownloadURL().then(downloadURL => {
//prepaImagesUrl+="+"+downloadURL;
prepaImagesUrl.push(downloadURL);
});
});
the problem is i am getting an array of the length of the number of uploaded files minus one (the legnth it should be equal to the number of uploaded files) and it has the same value (the same downloadurl)
. any help will be appreciated
Thank you.
I think the problem is with the promisies. I suggest you to use Promise.all and await. Therefore your code will be more reliable. Here is my solution to multiple file upload (adapt to your variable names):
const array = Array.from({ length: prepaImages.length }, (value, index) => index);
const uploadedImages = await Promise.all(array.map(async index => {
const image = prepaImages[index];
const metadata = { contentType: image.type };
const storageRef = firebase.storage().ref(`receipes/${category}/${title}/${uploadTime}${prepaImages[i].name}`);
const uploadTask = storageRef.put(image, metadata);
const url = await new Promise((resolve, reject) => {
uploadTask.on('state_changed', snapshot => {
const percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
$('#prepaImageUploadprogress').html(`${Math.round(percentage)}%`);
$('#prepaImageUploadprogress').attr('style', `width: ${percentage}%`);
}, error => reject(error),
async () => {
const downloadUrl = await uploadTask.snapshot.ref.getDownloadURL();
resolve(downloadUrl);
});
});
return { name: image.name, url };
}));
The uploadedImages will contains an array with the image names and download urls. You can make this without await of course, but I prefer this way.
UPDATE:
Here is my own code (without error handling) to achieve this, also, I need to mention that I'm using this with react, redux and using the firebase, firestore wrapper for redux redux-firestore and react-redux-firebase but these are just wrappers:
export const addNewWork = work => async (dispatch, getState, { getFirebase, getFirestore }) => {
const { files, ...restWork } = work;
const firebase = getFirebase();
const firestore = getFirestore();
const storageRef = firebase.storage().ref();
const array = Array.from({ length: files.length }, (value, index) => index);
const uploadedFiles = await Promise.all(array.map(async index => {
const file = files[index];
const metadata = { contentType: file.type };
const uploadTask = storageRef.child(`works/${file.name}`).put(file, metadata);
const url = await new Promise((resolve, reject) => {
uploadTask.on('state_changed', () => {}, error => reject(error), async () => {
const downloadUrl = await uploadTask.snapshot.ref.getDownloadURL();
resolve(downloadUrl);
});
});
return { name: file.name, url };
}));
await firestore.collection('works').add({
...restWork,
image: uploadedFiles[0], // Use only one image for the clean example
createdAt: new Date()
});
});

Categories

Resources