Self taught coder here. Hopefully I can explain this properly. I'm trying to create a recipe website where a user can upload their own custom recipe with images. The user submits a form and the info uploads to a database collection 'guides'. How does the file being uploaded to firebase storage send back a URL to react so that the form can record the URL into the steps array?
This is the logic for user to upload img in the form, inside Upload component:
const uploadFile = () => {
const name = new Date().getTime() + file.name
const storageRef = ref(storage, file.name)
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
setPercentage(progress)
switch (snapshot.state) {
case 'paused':
console.log('Upload is paused');
break;
case 'running':
console.log('Upload is running');
break;
default:
break;
}
},
(error) => {
console.log(error)
},
() => {
// Handle successful uploads on complete
getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
setData((prev) => ({...prev, img: downloadURL}))
});
}
);
}
file && uploadFile();
}, [file]);
Here is the logic to upload/submit form also inside the Upload component.
const handleAddGuide = async (e) => {
e.preventDefault();
const newId = uuidv4()
await setDoc(doc(db, "guides", newId), {
author: e.target.author.value,
categoryId: "100",
categoryName: e.target.categoryName.value,
// coverImg: imgURL,
id: newId,
name: e.target.name.value,
tags: e.target.tags.value,
steps: [
{
step: e.target.step.value,
img: file.url <---- I need this file to upload to a folder inside storage named dynamically after the name of the recipe ("e.target.name.value").
}
]
});
};
And this is where I initialize db and storage:
firebase.js
import { initializeApp } from "firebase/app";
import { getFirestore } from 'firebase/firestore'
import { getAuth } from "firebase/auth";
import "firebase/storage"
import { getStorage } from "firebase/storage";
const firebaseConfig = {
<details>
}
const app = initializeApp(firebaseConfig);
export const userAuth = getAuth()
export const db = getFirestore(app)
export const storage = getStorage(app);
Related
I'm trying to upload multiple images to firebase storage and store those urls in one firestore document as an array of strings, but I couldn't add all urls in same firestore document. Instead it's creating different documents for each firebase storage Url.
ImageUpload.js
import React, { useState } from "react";
import { toast } from "react-toastify";
import { db, storage } from "../firebase.config";
import { ref, uploadBytesResumable, getDownloadURL } from "firebase/storage";
import {collection ,addDoc ,arrayUnion ,FieldValue ,updateDoc} from "firebase/firestore";
import { useNavigate } from "react-router-dom";
import "./style.css";
function ImageUpload() {
const [allImages, setAllImages] = useState([]);
function handleAllImage(e) {
e.preventDefault();
setAllImages(e.target.files);
console.log(e.target.files);
}
function MutipleUpload(e) {
e.preventDefault();
try {
const docRef = collection(db, "products");
{
for (let i = 0; i < allImages.length; i++) {
const storageRef = ref(storage, `productImages/${allImages[i].name}`);
const uploadTask = uploadBytesResumable(storageRef, allImages[i], {
contentType: allImages[i].type,
});
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(progress);
},
(error) => toast.error(error.message),
() => {
getDownloadURL(uploadTask.snapshot.ref).then(
async (downloadURL) => {
await addDoc(docRef, {
imgUrl: arrayUnion(downloadURL),
});
}
);
}
);
}
}
toast.success("product added successfully!");
} catch (error) {
console.log(error.message);
}
}
return (
<>
<input
type="file"
multiple="multiple"
onChange={handleAllImage}
required
/>
<button onClick={MutipleUpload} type="submit" className="buy__btn mt-3">
Add Image
</button>
</>
);
}
export default ImageUpload;
Extra text cause error "It looks like your post is mostly code; please add some more details."
The problem is in this code:
getDownloadURL(uploadTask.snapshot.ref).then(
async (downloadURL) => {
await addDoc(docRef, {
imgUrl: arrayUnion(downloadURL),
});
}
);
Every time you call addDoc, Firestore create a new document in the collection.
If you instead want update an existing document, you have to:
Remember the document ID for the document you want to update.
Then call updateDoc on that document
To generate a document ID once and then update that all the time, have a look at the third code snippet in the documentation on adding a document:
In some cases, it can be useful to create a document reference with an auto-generated ID, then use the reference later. For this use case, you can call doc():
import { collection, doc, setDoc } from "firebase/firestore";
// Add a new document with a generated id
const newCityRef = doc(collection(db, "cities"));
// later...
await setDoc(newCityRef, data);
Applied to your use-case, that could look something like this:
const docRef = collection(db, "products");
const newDocRef = doc(docRef); // 👈 generate reference to a single new doc
{
for (let i = 0; i < allImages.length; i++) {
const storageRef = ref(storage, `productImages/${allImages[i].name}`);
const uploadTask = uploadBytesResumable(storageRef, allImages[i], {
contentType: allImages[i].type,
});
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(progress);
},
(error) => toast.error(error.message),
() => {
getDownloadURL(uploadTask.snapshot.ref).then(
async (downloadURL) => {
await setDoc(newDocRef, { // 👈 Call setDoc, since the doc may not exist yet
imgUrl: arrayUnion(downloadURL),
}, { merge: true }); // 👈 tell it to merge values into any existing doc
}
);
}
);
}
}
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.
I'm following a video tutorial about Nextjs and have seen that the version of Firebase he is using is out of date. I managed to follow all the steps reading the documentation until I got to this point.
Searched here and found some interesting answers on how to achieve the option to upload images to Firebase. Here the link
Have tried all solutions and none of them have worked. I get an error message:
FirebaseError: Firebase Storage: An unknown error occurred, please check the error payload for server response. (storage/unknown)
Bad Request
Here the code I'm trying:
import Image from "next/image";
import { useSession } from "next-auth/react";
import { FaceSmileIcon } from "#heroicons/react/24/outline";
import { VideoCameraIcon, PhotoIcon } from "#heroicons/react/20/solid";
import { useRef } from "react";
import { db, storage } from "../firebase";
import { collection, addDoc, serverTimestamp, doc, setDoc } from "firebase/firestore";
import { useState } from "react";
import {ref, uploadString, getDownloadURL, getStorage} from "firebase/storage";
function InputBox() {
const {data: session} = useSession();
const inputRef = useRef(null);
const filePickerRef = useRef(null);
const [imageToPost, setImageToPost] = useState(null);
const sendPost = async (e) => {
e.preventDefault();
if(!inputRef.current.value) return;
const colRef = collection(db, "posts")
await addDoc(colRef, {
message: inputRef.current.value,
name: session.user.name,
email: session.user.email,
image: session.user.image,
timestamp: serverTimestamp(),
}).then((document) => {
if(imageToPost) {
const storageRef = ref(storage, `posts/${document.id}`);
uploadString(storageRef, imageToPost, "data_url").then((snapshot) => {
getDownloadURL(snapshot.ref).then(URL => {
setDoc(doc(db, "posts", document.id),
{imageToPost: URL}, {merge: true}
);
});
})
removeImage();
}
})
inputRef.current.value = "";
};
const addImageToPost = (e) => {
const reader = new FileReader();
if(e.target.files[0]) {
reader.readAsDataURL(e.target.files[0]);
}
reader.onload = (readerEvent) => {
setImageToPost(readerEvent.target.result);
}
};
const removeImage = () => {
setImageToPost(null);
};
return ( <HERE THE REST OF THE CODE>
Don't be mad at me. I really tried to do my best to find a solution and to not post here.
Any help will be really appreciated because I can't figure out what is wrong, because as mentioned before I tried all options I found so far.
BTW also tried to assign storage to getStorage() before const storageRef = ref(storage, `posts/${document.id}`);
Like so:
.then((document) => {
if(imageToPost) {
const storage = getStorage();
const storageRef = ref(storage, `posts/${document.id}`);
<MORE CODE>
And my firebase.jsfile:
import { initializeApp } from 'firebase/app';
import { getStorage } from "firebase/storage";
const firebaseConfig = {
apiKey: "APIKEY-HERE",
authDomain: "AUTHDOMAIN-HERE",
projectId: "PROJECT-ID-HERE",
storageBucket: "STORAGE-BUCKET-HERE",
messagingSenderId: "MESSAGING-SENDER-HERE",
appId: "APPID-HERE"
};
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const storage = getStorage(app);
export { db, storage };
Try this, this worked for me: Firebase: "^9.0.0"
addDoc(dbInstance, {
message: inputRef.current.value,
name: session?.user?.name,
email: session?.user?.email,
image: session?.user?.image,
timestamp: serverTimestamp(),
}).then((doc) => {
if (imageToPost) {
const storageRef = ref(storage, `posts/${doc.id}`);
uploadString(storageRef, imageToPost, "data_url").then((snapshot) => {
getDownloadURL(snapshot.ref).then((url) => {
addDoc(dbInstance, { postImage: url });
});
});
removeImage();
}
});
inputRef.current.value = "";
Try this. I have used firebase v8.6.3.
const docRef = firebase.firestore().collection("colection_name").doc();
const storageRef = firebase.storage().ref();
const uploadTask = storageRef.child('/posts/' + imageToPost.name).put(imageToPost);
console.log(uploadTask)
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed'
(snapshot) => {
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
switch (snapshot.state) {
case firebase.storage.TaskState.PAUSED: // or 'paused'
console.log('Upload is paused');
break;
case firebase.storage.TaskState.RUNNING: // or 'running'
console.log('Upload is running');
break;
}
},
(error) => {
// A full list of error codes is available at
// https://firebase.google.com/docs/storage/web/handle-errors
switch (error.code) {
case 'storage/unauthorized':
// User doesn't have permission to access the object
break;
case 'storage/canceled':
// User canceled the upload
break;
// ...
case 'storage/unknown':
// Unknown error occurred, inspect error.serverResponse
break;
}
},
() => {
// Upload completed successfully, now we can get the download URL
uploadTask.snapshot.ref.getDownloadURL().then((downloadURL) => {
console.log('File available at', downloadURL);
docRef.set({
message: inputRef.current.value,
name: session.user.name,
email: session.user.email,
image: downloadURL,
});
});
}
);
If you don't want the upload progress and error then you can get rid of that codes and directly proceed to getDownloadURL.
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
Im new in web development . i tried to find an answer all over the internet in the past 24 hours without success and now im reaching out here
this is my err :
Uncaught TypeError: firebase__WEBPACK_IMPORTED_MODULE_3_.storage.ref is not a function
this is the firebase.js file :
import { initializeApp } from 'firebase/app';
import { getStorage } from 'firebase/storage';
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
const app = initializeApp(firebaseConfig);
export const storage = getStorage(app);
and im importing the storage in the component like that :
import {storage} from "../../firebase";
this is the upload func :
const upload = (items) => {
items.forEach((item) => {
const fileName = new Date().getTime() + item.label + item.file.name;
const uploadTask = storage.ref(`/items/${fileName}`).put(item.file);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
(snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log("Upload is " + progress + "% done");
},
(error) => {
console.log(error);
},
() => {
uploadTask.snapshot.ref.getDownloadURL().then((url) => {
setMovie((prev) => {
return { ...prev, [item.label]: url };
});
setUploaded((prev) => prev + 1);
});
}
);
});
};
any suggestions ?
You are using the syntax for the legacy namespaced Firebase JS SDK (v8 and older) with the newer modular Firebase SDK (v9 and newer).
Instead of storage.ref(), you need to be using ref(storage, path):
import { storage } from "../../firebase"
import { ref, uploadBytes } from "firebase/storage";
const upload = (items) => {
items.forEach((item, index) => {
const storageRef = ref(storage, `/items/${fileName}`);
const uploadTask = uploadBytes(storageRef, item.file);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 1000)/10;
console.log(`Upload #${index+1} is ${progress}% done`);
},
(error) => {
console.log(error);
},
() => {
console.log(`Upload #${index+1} is complete, fetching URL...`);
getDownloadURL(storageRef)
.then((url) => {
console.log(`Upload #${index+1} is now available at ${url}.`);
setMovie((prev) => {
return { ...prev, [item.label]: url };
});
setUploaded((prev) => prev + 1);
})
.catch((error) => {
console.log(error);
});
}
);
});
}
I encourage you to read the documentation completely and read the migration guide so that you can see which SDK version the code you are working off of was built for.
Note: You should further improve this code to handle errors better, such as set an error message visible to the user for failed uploads.