I get same url for multiple file on Firebase Storage - javascript

When I try to send file to Firebase Storage and getDownloadUrl for each files, I notice that all file are upload but I get the url of last File.
This is the function.
var files = this.state.image
var image, fileExtension, imageName, uploadTask
for (var i = 0; i < this.state.image.length; i++) {
image = files[i]
console.log(this.state.image)
fileExtension = '.' + image.name.split('.').pop();
imageName = Math.random().toString(36).substring(7) + new Date().getTime() + [i] + fileExtension
uploadTask[i] = storage.ref(`images/${imageName}`).put(image);
console.log(imageName)
uploadTask.on(
"state_changed",
snapshot => {
// progress function ...
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
this.setState({ progress });
},
error => {
// Error function ...
toast('ldjddkj')
console.log(error);
},
async () => {
// complete function ...
await storage
.ref("images")
.child(imageName)
.getDownloadURL()
.then(imgUrl => {
console.log(imgUrl)
this.setState((prevState) => ({
imgUrl: [...prevState.imgUrl,
imgUrl
],
}
))
console.log(this.state.imgUrl)
})
}
);
}

Try this.
uploadTask.on(
"state_changed",
(snapshot) => {
// progress function ...
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 100
);
this.setState({ progress });
},
(error) => {
// Error function ...
toast("ldjddkj");
console.log(error);
}
);
uploadTask
.then((snapshot) => {
return snapshot.ref.getDownloadURL();
})
.then((imgUrl) => {
storage
.ref("images")
.child(imageName)
.getDownloadURL()
.then((imgUrl) => {
console.log(imgUrl);
this.setState((prevState) => ({
imgUrl: [...prevState.imgUrl, imgUrl],
}));
console.log(this.state.imgUrl);
});
});

Related

How to show progress bar while uploading multiple images to Firebase Storage?

I made function that upload multiple images to storage and save links to document but i can't figure out how to monitor progress.
const getURLS = async () => {
const promises = [];
images &&
images.map((image) => {
const storageRef = ref(storage, `images/${image?.file?.name + v4()}`);
promises.push(
uploadBytesResumable(storageRef, dataURLtoBlob(image.data_url)).then((uploadResult) => {
return getDownloadURL(uploadResult.ref);
})
);
});
const urls = await Promise.all(promises);
try {
await addDoc(collection(db, 'posts'), {
message: data,
createdAt: serverTimestamp(),
createdBy: user,
likes: [],
comments: [],
images: urls,
}).then(() => {
setData('');
setImages([]);
});
} catch (err) {
console.log(err);
}
};
How to add firebase monitor upload progress to this function?
I tried different ways but it dosn't work
thx for help
The uploadBytesResumable() does not return a promise but an UploadTask. You can iterate over all the images selected and track their progress individually as shown below:
function App() {
const [images, setImages] = useState([])
const [progress, setProgress] = useState([])
const handleFileChange = (e) => {
const files = e.target.files
const newImages = []
for (let i = 0; i < files.length; i++) {
newImages.push(files[i])
}
setImages(newImages)
}
const handleUpload = async () => {
for (let i = 0; i < images.length; i++) {
const image = images[i]
const storageRef = ref(storage, `images/${image.name}`)
const uploadTask = uploadBytesResumable(storageRef, image)
uploadTask.on('state_changed', (snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
setProgress((prevProgress) => {
const newProgress = [...prevProgress]
newProgress[i] = progress.toFixed(2)
return newProgress
})
}, (error) => {
console.log(error)
}, async () => {
const imageUrl = await getDownloadURL(uploadTask.snapshot.ref)
// Add to Firestore
})
}
}
return (
<div className="App">
<input type="file" multiple onChange={handleFileChange} />
<button onClick={handleUpload}>Upload</button>
{progress.map((progress, i) => (
<div key={i}>{images[i].name} {progress}%</div>
))}
</div>
)
}
Each file is uploaded separately so you would have to implement some logic to get all URLs together and add in Firestore document like you can call a function when progress of all uploads i.e. all items in progress array become 100.

Firebase: Error .getDownloadURL is not a function

I want to get the link to the uploaded image with .getDownloadURL() but I end up with an error:
Uncaught (in promise) TypeError: uploadTask.snapshot.ref.getDownloadURL is not a function
If I remove .getDownloadURL() and .ref then everything works and I end up with:
Download URL Object { bytesTransferred: 120745, ... ref: {…} }
Author source code:
import {upload} from './upload'
import { initializeApp } from "firebase/app";
import 'firebase/storage'
import { getStorage, ref, uploadBytesResumable } from "firebase/storage"
// Your web app's Firebase configuration
const firebaseConfig = {
*code Firebase*
}
const app = initializeApp(firebaseConfig)
const storage = getStorage(app)
upload('#file', {
multi: true,
accept: ['.png', '.jpg', '.jpeg', '.gif'],
onUpload(files, blocks) {
files.forEach((file, index) => {
const storageRef = ref(storage, `images/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed', snapshot => {
const percentage = ((snapshot.bytesTransferred / snapshot.totalBytes) * 100).toFixed(0) + '%'
const block = blocks[index].querySelector('.preview-info-progress')
block.textContent = percentage
block.style.width = percentage
}, error => {
console.log(error)
}, () => {
uploadTask.snapshot.ref.getDownloadURL().then(url => { // << problem
console.log('Download URL', url)
})
})
})
}
})
Thank you!
You're mixing the older, namespace syntax of SDK versions 8 and before with the newer, modular syntax of SDK versions 9 and above. That won't work, so you should pick one syntax/version and stick to that.
The modular syntax for getting the download URL is:
getDownloadURL(storageRef).then(url => {
console.log('Download URL', url)
})
Unrelated to that, I usually find the code easier to read when I use the fact that the upload task is a promise itself. With that, the code becomes:
files.forEach((file, index) => {
const storageRef = ref(storage, `images/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed', snapshot => {
const percentage = ((snapshot.bytesTransferred / snapshot.totalBytes) * 100).toFixed(0) + '%'
const block = blocks[index].querySelector('.preview-info-progress')
block.textContent = percentage
block.style.width = percentage
})
uploadTask.then(() => {
getDownloadURL(ref).then(url => {
console.log('Download URL', url)
})
})
})
And with async/await that becomes:
files.forEach((file, index) => async {
const storageRef = ref(storage, `images/${file.name}`);
const uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed', snapshot => {
const percentage = ((snapshot.bytesTransferred / snapshot.totalBytes) * 100).toFixed(0) + '%'
const block = blocks[index].querySelector('.preview-info-progress')
block.textContent = percentage
block.style.width = percentage
})
await uploadTask;
const url = await getDownloadURL(ref)
console.log('Download URL', url)
})
This solution from the official Firebase documentation helped.
...
onUpload(files, blocks) {
files.forEach((file, index) => {
const storage = getStorage();
const starsRef = ref(storage, `images/${file.name}`);
const uploadTask = uploadBytesResumable(starsRef, file);
uploadTask.on('state_changed', snapshot => {
const percentage = ((snapshot.bytesTransferred / snapshot.totalBytes) * 100).toFixed(0) + '%'
const block = blocks[index].querySelector('.preview-info-progress')
block.textContent = percentage
block.style.width = percentage
}, error => {
console.log(error)
}, () => {
getDownloadURL(starsRef)
.then((url) => {
console.log('URL:', url);
})
})
})
}
})
I don't know if this is the right solution, and while it works, I'll investigate Firebase further.

firebase storage ref is not a function

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.

Uploading image to firebase using expo react-native

I am working on an app and I am using expo, I want to make sure each user can upload an image to firebase, and later publish this image on the profile page.
Using expo this is how I upload images:
const pickImage = async () => {
let pickerResult = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
aspect: [4, 3],
quality: 1,
});
console.log(pickerResult);
handleImagePicked(pickerResult);
};
the result in the console is:
Object {
"cancelled": false,
"height": 312,
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fallergyn-app-77bfd368-65fd-43f9-8c34-9c35cef42c25/ImagePicker/daaa229c-c352-4994-ae18-ca2dbb3534ce.jpg",
"width": 416,
}
and this is how I upload to the firebase:
const handleImagePicked = async (pickerResult) => {
try {
if (!pickerResult.cancelled) {
setImage(pickerResult.uri);
await uploadImageAsync(pickerResult.uri);
console.log("done");
}
} catch (e) {
console.log(e);
alert("Upload failed, sorry :(");
} finally {
}
};
async function uploadImageAsync(uri) {
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function () {
resolve(xhr.response);
};
xhr.onerror = function (e) {
console.log(e);
reject(new TypeError("Network request failed"));
};
xhr.responseType = "blob";
xhr.open("GET", uri, true);
xhr.send(null);
});
const ref = firebase
.storage()
.ref()
.child("images" + Math.random());
const snapshot = await ref.put(blob);
// We're done with the blob, close and release it
blob.close();
return await snapshot.ref.getDownloadURL();
}
this code works it saves the path of the image this: "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540anonymous%252Fallergyn-app-77bfd368-65fd-43f9-8c34-9c35cef42c25/ImagePicker/daaa229c-c352-4994-ae18-ca2dbb3534ce.jpg" in the firebase under user collection using the uid of the user.
I am not sure if this is good, because I want to make sure the image itself is uploaded to firebase, I saw some threads in StackOverflow regarding this issue either too old or no answers, so I am hoping to get some sort of solution to what I need to do.
if I use
const ref = firebase
.storage()
.ref()
.child("images" + Math.random());
.putFile(uri);
this tells me that putFile is not a function. the same with put(uri)
Try this one. This function returns the path of the saved image from firebase which you will store in the user's document instead.
const handleImagePicked = async (pickerResult) => {
if (!pickerResult.cancelled) {
setImage(pickerResult.uri);
const result = await uploadImageAsync(pickerResult.uri);
if(result) {
console.log('success');
//save the result path to firestore user document
return;
}
alert("Upload failed, sorry :(");
}
};
export const uploadImageAsync = async (uri: string) => {
let filename = uri;
if (Platform.OS === 'ios') {
filename = uri.replace('file:', '');
}
const ext = filename.split('.').pop();
const path = `images/${id}.${ext}`;
const ref = firebase.storage().ref(path);
try {
const response = await fetch(filename);
const blob = await response.blob();
await ref.put(blob);
return path;
} catch {
return null;
}
};
This worked for me , using rn-fetch-blob
import launchImageLibrary from 'react-native-image-picker';
import RNFetchBlob from 'rn-fetch-blob';
import storage from '#react-native-firebase/storage';
const pickImage = () => {
let options = {
mediaType: 'photo',
quality: 0.5,
};
launchImageLibrary(options, (response) => {
console.log('Response = ', response);
uploadImagePicked(response);
});
};
const uploadImagePicked = (response) => {
if (response.fileName) {
const fileName = response.fileName;
var storageRef = storage().ref(`receiptImages/${fileName}`);
RNFetchBlob.fs.readFile(response.uri , 'base64')
.then(data => {
storageRef.putString(data, 'base64', {contentType:"image/jpg"})
.on(
storage.TaskEvent.STATE_CHANGED,
snapshot => {
console.log("snapshot: " + snapshot.state);
console.log("progress: " + (snapshot.bytesTransferred / snapshot.totalBytes) * 100);
if (snapshot.state === storage.TaskState.SUCCESS) {
console.log("Success");
}
},
error => {
console.log("image upload error: " + error.toString());
},
() => {
storageRef.getDownloadURL()
.then((downloadUrl) => {
console.log("File available at: " + downloadUrl);
})
})
})
.catch(error => {
console.log(error);
})
}
else {
console.log("Skipping image upload");
}
}

Unable to use variables inside promise

I am trying to updload a picture to firebase storage, no problem with that.
However when I try to set the state with the image URL and some other things that I am getting from a form, my variables (section and price) are empty inside the promise. Below my code:
handleForm = e =>{
e.preventDefault();
const {section, price, image} = e.target
const file = image.files[0]
const pictureName = userInformation.name.replace(/ /g, "_");
if(file.size <= 1000000){
const myRoute = '/img/userPictures/' + pictureName;
const storageRef = firebase.storage().ref(myRoute);
const task = storageRef.put(file);
task.on('state_changed', snapshot =>{
console.log('Uploaded');
}, error =>{
console.log(error.message)
}, () =>{
task.snapshot.ref.getDownloadURL().then(downloadURL =>{
this.setState({
information: this.state.data.concat([{
section: section.value,
price: price.value,
image:downloadURL}])
})
});
})
e.currentTarget.reset();
this.notifySuccess('Information uploaded');
}else{
this.notifyError('Image should be less than 1 MB')
}
}
Where do I have the error? thanks!
It's because you are using e.currentTarget.reset() outside the callback.
Try to put it inside on the success of your callback, it should work as expected
(As shown below)
handleForm = e => {
e.preventDefault()
const {section, price, image} = e.target
const file = image.files[0]
const pictureName = userInformation.name.replace(/ /g, '_')
if (file.size <= 1000000) {
const myRoute = '/img/userPictures/' + pictureName
const storageRef = firebase.storage().ref(myRoute)
const task = storageRef.put(file)
task.on(
'state_changed',
snapshot => {
console.log('Uploaded')
},
error => {
console.log(error.message)
},
() => {
task.snapshot.ref.getDownloadURL().then(downloadURL => {
this.setState({
information: this.state.data.concat([
{
section: section.value,
price: price.value,
image: downloadURL
}
])
})
})
e.currentTarget.reset()
this.notifySuccess('Information uploaded')
}
)
} else {
this.notifyError('Image should be less than 1 MB')
}
}

Categories

Resources