I use Firebase Storage with React. I have an array with multiple images. For every single image in it I do an upload to Firebase. Once I get the downloadUrl back, I want to store it in a hook array for further use. However, my array is always empty. Why ?
If I use the useEffect hook as soon as the array changes, I get an output every time the downloadUrl is inserted into the array. However: One line per URL and not an array with the URLs
const [uploadBilder, setUploadBilder] = useState([]);
await Promise.all(
mehrBilder?.map((bild) => {
const storageRef = ref(
storage,
`${aktuellerUser._id}/artikelBilder/` + bild.name
);
const uploadTask = uploadBytesResumable(storageRef, bild);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
}
},
(error) => {
console.log(error);
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setUploadBilder((prev) => [...prev, downloadURL]);
console.log(uploadBilder); //EMPTY
});
}
);
})
);
useEffect(() => {
console.log(uploadBilder) //count 3 = right!
},[uploadBilder]}
Related
i wanna save my uploaded images in a folder called "course-cover" in firebase storage
this is my code that saves uploaded images in the route of storage directly but instead of thet i wanna save them in the "course-cover" folder
async function storeCoverCourse(coverCourse) {
return new Promise((resolve, reject) => {
const storage = getStorage();
const filename = `${coverCourse.name}-${uuidv4()}`;
const storageRef = ref(storage, filename);
const uploadTask = uploadBytesResumable(storageRef, coverCourse);
uploadTask.on(
"state_changed",
(snapshot) => {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
}
},
(error) => {
// Handle unsuccessful uploads
reject(error);
},
() => {
// Handle successful uploads on complete
// For instance, get the download URL: https://firebasestorage.googleapis.com/...
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
resolve(downloadURL);
});
}
);
});
}
const imgUrls = await Promise.all(
[...cover].map((coverCourse) => storeCoverCourse(coverCourse))
).catch((error) => {
toast.error("image not uploaded");
return;
});
const formDataCopy = {
...formData,
imgUrls,
timestamp: serverTimestamp(),
};
delete formDataCopy.cover;
await addDoc(collection(db, "courses"), formDataCopy);
The storageRef is a StorageReference and you essentially specify path of your file with it. If your path contains /, that'll be like a directory. Try:
const filename = `course-cover/${coverCourse.name}-${uuidv4()}`;
const storageRef = ref(storage, filename);
It's not actually a folder under the hood but just a namespace. For more details, see How to create a folder in Firebase Storage?
In addition to the solution that #Dharmaraj covered in their answer, you should also get rid of the problematic use of the Promise constructor with Promise returning functions. It can be eliminated because the uploadTask object is a thenable/promise-like object.
async function storeCoverCourse(coverCourse) {
const storage = getStorage();
const filename = `course-cover/${coverCourse.name}-${uuidv4()}`;
const storageRef = ref(storage, filename);
const uploadTask = uploadBytesResumable(storageRef, coverCourse);
uploadTask.on(
"state_changed",
(snapshot) => {
// Observe state change events such as progress, pause, and resume
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
switch (snapshot.state) {
case "paused":
console.log("Upload is paused");
break;
case "running":
console.log("Upload is running");
break;
}
}
);
return uploadTask
.then(snapshot => snapshot.ref.getDownloadURL());
}
As I work on an Instagram clone using React and Firebase, it doesn't seem to be uploading images to my Firebase database.
In my image upload component, I have a progress bar, a caption, and an image upload button. In the first instance, it uploads a null or empty image and in the second instance, it uploads the image that I selected at the beginning or first instance.
Here is my image component
const date = new Date().getTime();
let storageRef = ref(storage, `post/${date}.png`);
const upload = (e) => {
e.preventDefault();
if (postimg && postcaption) {
const uploadTask = uploadBytesResumable(storageRef, postimg);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
setprogress(progress);
},
(error) => {
// Handle unsuccessful uploads
},
() => {
postUpload();
}
);
} else {
console.log("all feild are mendatory ");
}
};
const postUpload = async () => {
let url = await getDownloadURL(storageRef);
setPosturl(url);
console.log(Posturl);
console.log("i am api link " + Posturl);
let post = await addDoc(posts, {
caption: postcaption,
avatar: profileImg,
post: Posturl,
username: "username",
});
setpostcaption("");
setprogress(0);
alert("i am upload ");
};
Firebase database doesn't show anything being uploaded to the "posts" collection and the second instance the image url at post that I selected at first instance isn't being uploaded.
I am not sure if that is an issue with my ImageUpload or Firebase.
I have a composable in vue that uploads data to firebase storage. It works, but I cannot get the data out of the composable. I think it has something to do with a return or where I'm defining terms?
The code below is built around the documentation available for firebase Storage (https://firebase.google.com/docs/storage/web/upload-files).
useStorage.js
import { ref } from "vue";
import { projectStorage } from "../firebase/config";
import {
uploadBytesResumable,
getDownloadURL,
ref as storageRef,
} from "#firebase/storage";
const useStorage = () => {
const error = ref(null);
const url = ref(null);
const filePath = ref(null);
const uploadImage = async (file) => {
filePath.value = `images/${file.name}`;
const storageReference = storageRef(projectStorage,
filePath.value);
const uploadTask =
uploadBytesResumable(storageReference, file);
await uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes)
* 100;
console.log("Upload is " + progress + "% done");
},
(err) => {
console.log(err);
error.value = err.message;
},
() => {
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL)
=> {
console.log("File available at", downloadURL);
url.value = downloadURL;
console.log(url.value); <--HAS CORRECT VALUE
return url.value; <--DOESNT DO ANYTHING
});
}
);
console.log(url.value);
};
return { url, filePath, error, uploadImage }; <--URL IS
NOT RETURNING OUT OF THIS COMPOSABLE
};
export default useStorage;
A simpeler approach would be to await getDownloadUrl: url.value = await getDownloadURL(uploadTask.snapshot.ref). Then, you can get rid of the .then on that function. Right now, return url.value is assigned to nothing.
Warning: Also write some sort of catch - in case something would go wrong - in a production environment.
I have some code to open the camera with React Native and save the image using FireBase, but it gives me an error saying filePath expects a string value.
Here is my code:
const openCamera = ()=>{
launchCamera({quality:0.5},(fileobj) =>{
console.log(fileobj)
const uploadTask = storage().ref().child(`/items/${Date.now()}`).putFile(fileobj.uri)
uploadTask.on('state_changed',
(snapshot) => {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
if (progress == 100){alert("Uploaded")}
},
(error) => {
alert("something went wrong")
},
() => {
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) =>{
console.log(downloadURL)
setImage(downloadURL)
});
}
);
})
}
The launchCamera method accepts a callback that will be called with a response object. This response object has four keys; didCancel, errorCode, errorMessage, and assets.
assets is an array of the selected media, so you'll need to grab the first one.
const openCamera = () => {
launchCamera({ quality: 0.5 }, (result) => {
if (result.errorCode || result.didCancel) {
return console.log('You should handle errors or user cancellation!');
}
const img = result.assets[0];
const uploadTask = storage()
.ref()
.child(`/items/${Date.now()}`)
.putFile(img.uri);
uploadTask.on(
'state_changed',
(snapshot) => {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
if (progress == 100) {
alert('Uploaded');
}
},
(error) => {
alert('something went wrong');
},
() => {
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
console.log(downloadURL);
setImage(downloadURL);
});
},
);
});
};
I saw your problem and I have a solution for you .
I don't know why but please try to use like this once :
fileobj.assets[0].uri
I'm facing yet another issue.
I'm using firebase db to store text and firebase storage to store files. And here comes my issue.
Q: How to fetch a correct image from storage when fetching particular element from database?
Here's my attempt:
const storageRef = firebase.storage().ref('companyImages/companyImage' + 123);
^^^^^^^^^^^^ I dont have access to id yet :(
const task = storageRef.put(companyImage);
task.on('state_changed', () => {
const percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
// ^^^^^^^^^^^^ not sure if i even need this
}, (err) => {
console.log(err);
}, () => {
firebase.database().ref('offers').push(values);
^^^^^^^^^^^^^^^ now I could retrieve id from it with .key but its too late
});
As you can see, first what Im doing is uploading the image and when it's succesful, Im starting to upload the data to database.
Still, it doesnt work as it is supposed to. When uploading image I have to name it with a correct id to retrieve it easily later, in components.
It may look a lil bit complex but will appreciate any kind of help. Any suggestion or hint.
Should I firstly upload data to DB and then image to the storage?
You can generate the push ID before you upload the file – you can also just save the download URL of the returned snapshot at task.snapshot.downloadURL so you don't have to retrieve the file from storage using the storage ref.
const offerRef = firebase.database().ref('offers').push();
const storageRef = firebase.storage().ref(`companyImages/${offerRef.key}`);
const task = storageRef.put(companyImage);
task.on('state_changed', (snap) => {
const percentage = (snap.bytesTransferred / snap.totalBytes) * 100;
}, (error) => {
console.log(err);
}, () => {
offerRef.set(values);
});
I would suggest using .getDownloadURL(). Then push all your uploadedfileDownloadURL's into an object or array and then store that into your database. So in the future you can access this object or array from, lets say your user/ProfilePHotos, and then in your app level code you can just use the DownloadURL as a uri links inside an image tag!
In this example I am using react-native, I upload multiple photos, save the download URL each time in an array, then set the array to firebase under the users account.
export const userVehiclePhotoUploadRequest = (photos, user, year) => dispatch => {
console.log('Inside vehiclePhotoUpload Actions', photos, user)
let referenceToUploadedPhotos = [];
return new Promise((resolve, reject) => {
photos.map(ele => {
let mime = 'application/octet-stream'
let uri = ele.uri
let uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
let sessionId = new Date().getTime()
let uploadBlob = null
let imageRef = firebase.storage().ref('vehicleImages/' + `${user.account.uid}`).child(`${sessionId}`)
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
referenceToUploadedPhotos.push(url)
console.log('ARRAY OF URLS WHILE PUSHING', referenceToUploadedPhotos)
resolve(url)
})
.catch((error) => {
reject(error)
})
})
})
.then(() => {
//I did this to not go home until photos are done uploading.
let vehicles;
firebase.database().ref('users/' + user.account.uid + `/allVehicles/allVehiclesArray`).limitToFirst(1).once('value').then(function (snapshot) {
// ******** This method is straight from their docs ********
// ******** It returns whatever is found at the path xxxxx/users/user.uid ********
vehicles = snapshot.val();
}).then(() => {
console.log('ARRAY OF URLS BEFORE SETTING', referenceToUploadedPhotos)
// let lastVehicle = vehicles.length - 1;
firebase.database().ref('users/' + user.account.uid + `/allVehicles/allVehiclesArray/` + `${Object.keys(vehicles)[0]}` + `/photosReference`).set({
referenceToUploadedPhotos
}).then(() => {
dispatch(loginRequest(user.account))
})
})
})
};
And then in your code, lets say inside a map of the user's information...
{ ele.photosReference !== undefined ? dynamicAvatar = { uri: `${ele.photosReference.referenceToUploadedPhotos[0]}` } : undefined }