How to get image filename from Firebase Storage? - javascript

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;
};

Related

How to save image url from Firebase storage to realtime databse using javascript?

I was able to get the downloadURL of the image but i need to save it to my realtime database. Please help. Here is the code that i tried.
const storageRef = sRef(storage, 'images/' + 'announcement/');
const file_ann = document.getElementById("image_ann").files[0];
const name_ann = file_ann.name;
const metadata_ann = {
contentType: file_ann.type
}
const uploadAnnouncement = uploadBytesResumable(storageRef, file_ann, metadata_ann);
getDownloadURL(uploadAnnouncement.snapshot.ref).then((announcementURL) => {
console.log('File available at', announcementURL);
})
const announcementimg = document.getElementById("image_ann").value;
set(ref(database, 'page-content/' + 'announcement/'), {
announcementimg: announcementURL,
})
.then(() => {
window.alert("Announcement Updated!");
})
.catch((error) => {
window.alert("Error!");
});
Getting the download URL is (like most modern cloud API calls) an asynchronous operation. This means that any code that needs the announcementURL has to be inside the then() block that is called when that URL is available.
So:
getDownloadURL(uploadAnnouncement.snapshot.ref).then((announcementURL) => {
console.log('File available at', announcementURL);
const announcementimg = document.getElementById("image_ann").value;
set(ref(database, 'page-content/' + 'announcement/'), {
announcementimg: announcementURL,
})
.then(() => {
window.alert("Announcement Updated!");
})
.catch((error) => {
window.alert("Error!");
});
})

How to remove images from Firebase Storage?

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()

Download multiple files from Firebase in ReactJS

I have several folders in firebase storage that contain images (each folder corresponds to an entity).https://i.stack.imgur.com/s9ZpX.png
When I want to download the url of each entity, what firebase does is bring me all the images that it has in the storage even if it does the forEach to each entity.
useEffect(() => {
setLoading(true);
Promise.all([getRooms(), getLocation()])
.then((values) => {
const roomsSnapshot = values[0];
const rooms = [];
const pUrl = [];
roomsSnapshot.forEach((doc) => {
const splitAddress = doc.data().address.split(", ");
formatedAddress.current = splitAddress[1] + " " + splitAddress[0];
//Download from storage//
storage
.ref(`${doc.data().roomPhotoId}/`)
.list()
.then(function (result) {
result.items.forEach((imageRef) => {
imageRef.getDownloadURL().then((url) => {
console.log(url)
roomItems.push(url)
});
});
setRoomsImagesCounter(roomsImagesCounter + 1);
// no photos scenario
if (result.items.length === 0) {
setLoading(false);
}
});
//pushing entity data//
rooms.push({
...doc.data(),
id: doc.id,
photosURL: roomItems,
shortAddress: formatedAddress.current,
coordsAddress: coords.current,
});
});
console.log(rooms);
setRooms(rooms);
// getPhotos(rooms);
setLoading(false);
})
.catch((err) => {
console.log("Error getting getting rooms or user role", err);
});
// eslint-disable-next-line react-hooks/exhaustive-deps }, []);
I don't know why firebase brings me all the images instead of bringing me the images separately for each entity.
I hope you have understood and I await your answers or any questions you have.
Thanks!
UPDATE
This is the console.log(rooms) https://i.stack.imgur.com/rQkoB.png
Can you try to create the storage ref like this:
// Create a reference under which you want to list
var listRef = storage.ref().child(`${doc.data().roomPhotoId}/`);
// Find all the prefixes and items.
listRef.listAll()
.then((res) => {
res.prefixes.forEach((folderRef) => {
// All the prefixes under listRef.
// You may call listAll() recursively on them.
});
res.items.forEach((itemRef) => {
// All the items under listRef.
});
}).catch((error) => {
// Uh-oh, an error occurred!
});
Make sure to use listAll.
Be aware that:
root.child('images').listAll() will return /images/uid as a prefix.
root.child('images/uid').listAll() will return the file as an item.
You can find more about it here.
Your code with grouping the downloadURLS:
useEffect(() => {
setLoading(true);
Promise.all([getRooms(), getLocation()])
.then((values) => {
const roomsSnapshot = values[0];
const rooms = [];
const pUrl = [];
roomsSnapshot.forEach((doc) => {
const splitAddress = doc.data().address.split(", ");
formatedAddress.current = splitAddress[1] + " " + splitAddress[0];
//Download from storage//
rooms[`${doc.data().roomPhotoId}`]=[]
storage
.ref(`${doc.data().roomPhotoId}/`)
.list()
.then(function (result) {
result.items.forEach((imageRef) => {
imageRef.getDownloadURL().then((url) => {
console.log(url)
roomItems.push(url)
});
});
setRoomsImagesCounter(roomsImagesCounter + 1);
// no photos scenario
if (result.items.length === 0) {
setLoading(false);
}
});
//pushing entity data//
rooms[`${doc.data().roomPhotoId}`].push({
...doc.data(),
id: doc.id,
photosURL: roomItems,
shortAddress: formatedAddress.current,
coordsAddress: coords.current,
});
});
console.log(rooms);
setRooms(rooms);
// getPhotos(rooms);
setLoading(false);
})
.catch((err) => {
console.log("Error getting getting rooms or user role", err);
});
// eslint-disable-next-line react-hooks/exhaustive-deps }, []);

Firebase upload function works differently everytime

I have a form to upload a new item, that has a few fields and an image.
The way I've built the function is that it first sets the new document in Firestore, then it uploads the image, and then it resets the form.
The problem is that it doesn't always work. Sometimes the image gets uploaded and sometimes it isn't even if the form was reset (the reset is conditioned with the image upload).
It's not consistent so I can't figure out what exactly is happening.
This is the upload function:
export const newItem = (values, image, setValues) => () => {
const newDoc = db.collection("items").doc();
newDoc.set({ ...values, id: newDoc.id }).then(() => {
storageRef
.child(`images/items/${newDoc.id}`)
.put(image)
.then(result => {
console.log(result);
setValues({});
});
});
};
I call it is as follows:
newItem({ ...values, is_public }, imageObj, setValues);
Then I have this cloud function that addes the url for the newly uploaded file to the new document (but I don't think the issue is there, because when I say the image wasn't uploaded, then I mean I don't even see it in storage):
exports.writeFileToDatabase = functions.storage.object().onFinalize(object => {
const bucket = defaultStorage.bucket();
const path = object.name as string;
const file = bucket.file(path);
return file
.getSignedUrl({
action: "read",
expires: "03-17-2025"
})
.then(results => {
const url = results[0];
const silcedPath = path.split("/", 3);
switch (silcedPath[1]) {
case "user-avatars":
return db
.collection("users")
.doc(silcedPath[2])
.set({ avatar: url }, { merge: true });
case "items":
return db
.collection("items")
.doc(silcedPath[2])
.set({ image: url }, { merge: true });
default:
return null;
}
});
});
EDIT:
this is how I choose the file:
<input
id="image"
className="new-item__upload"
type="file"
onChange={handleImageChange}
/>
Then this is handleImageChange:
const handleImageChange = e => {
if (e.target.files[0]) {
const image = e.target.files[0];
setSelectedImage(URL.createObjectURL(image));
setImageObj(image); // This is what end up being given to the function to upload
}
};
You need to correctly chain the promises returned by the Firebase asynchronous operations (set() and put()) as follows:
export const newItem = (values, image, setValues) => () => {
const newDoc = db.collection("items").doc();
newDoc.set({ ...values, id: newDoc.id })
.then(() => {
return storageRef //Here return the Promise returned by the put() method
.child(`images/items/${newDoc.id}`)
.put(image);
})
.then(snapshot => {
console.log(snapshot);
setValues({});
})
.catch(e => {
console.error(e.message);
//You should throw an error here, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Control_flow_and_error_handling
});
};
It is also recommended to add a catch() method call at the end of your Promises chain, in order to get more details in case of error.

Upload data to firebase storage and Firebase database from array in react native

I have an array that contains the local location of some images as shown below.
imagePath=
["file:///data/user/0/com.app.taamoutlet/cache/react-native-image-crop-picker/22536474236950.png","file:///data/user/0/com.app.taamoutlet/cache/react-native-image-crop-picker/22583225016770.png
"]
I have done uploading for a single image.
As per the code you have seen below I want to upload the images to firebase storage. I want to upload each image in the array to a single folder. And then collect the downloadable URL and each image and store that information under a single product as you see
() => {
const fs = RNFetchBlob.fs
const uid = "flyers/" + this.state.productUid;
const imageRef = firebase.storage().ref(uid).child(Math.random()+".jpg") //string "dp1.jpg"
let mime = 'image/jpg'
//------------------------------------------------
// coverting to base64
fs.readFile(this.state.imagePath, 'base64')
.then((data) => {
//console.log('data='+data);
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
//uplaoding Image
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
//getting url
return imageRef.getDownloadURL()
})
.then((url) => {
urls = url;
console.log('urls=' + urls)
//================================
try {
alert("Uploading Flyer" + this.state.title)
//-------------------------------------------
//----------Inserting Data to Database--------
usersTable = 'flyers/' + this.state.productUid,
console.log(usersTable)
firebase.database().ref(usersTable).set(
{
title: this.state.title,
description: this.state.description,
imageUrls: url,
storename: this.state.storename,
user: asyncValue,
longitude: this.state.longitude,
latitutde: this.state.latitutde
}
).then(
alert(this.state.title + " flyer sucessfully uploaded")
)
//--------------------------------------------
}
catch (error) {
this.setState({ loading: false })
this.setState({ disabled: false })
console.log(error.toString())
alert(error.toString())
}
//================================
})
}
As mentioned by Renaud, you have to use Promise.all. Please check out this example:
const promises = [fs.readFile(this.state.imagePath, 'base64'),...];
return Promise.all(promises).then((arrayOfResults) => {
const furtherPromises = [...]
return Promise.all(furtherPromises)
.then((uploadedFiles) => {
// action required
})
.catch((error) => {
// action required
});
}).catch((error) => {
// action required
});

Categories

Resources