Can't retrieve download url from firebase storage - reactjs

I'm trying to retrieve download url so i can pass it down in promise in the function i'm calling.
In the storage image is uploaded but somehow i'm getting error on the line where i console.log - 404 error like it does not exist ( but it does )
uploadFile = (file, metadata) => {
const pathToUpload = this.state.channel.id;
const ref = this.props.messagesRef;
const filePath = `chat/public/${uuidv4()}.jpg`; // uuid is a function that creates random string
this.setState({
uploadState: 'uploading',
uploadTask: this.state.storageRef.child(filePath).put(file,metadata)
},
() => {
this.state.uploadTask.on('state_changed', snap => {
const percentUploaded = Math.round((snap.bytesTransferred / snap.totalBytes) * 100)
this.setState({percentUploaded})
},
err => {
console.error(err)
this.setState({
errors: this.state.errors.concat(err),
uploadState: 'error',
uploadTask: null
})
})
},
() => {
this.state.uploadTask.snapshot.ref.getDownloadURL().then(downloadUrl => {
console.log(downloadUrl) // get error
this.sendFileMessage(downloadUrl, ref, pathToUpload)
})
.catch(err => {
console.error(err)
this.setState({
errors: this.state.errors.concat(err),
uploadState: 'error',
uploadTask: null
})
})
}
)
};
If you need more code let me know, but on this point where i log error it's where the problem is.
Simply url of posted image in the DB can't be retrieved, i tried with storage rules but there everything looks fine.
EDIT: With state_changed listener state doesn't change - whole time it stays at 'uploading' while image is being stored in storage

It looks like you're not waiting for the upload to fully complete before requesting the download URL. This is usually what a 404 means with this sort of code. You're going to have to use the promise returned by storageRef.child(filePath).put(file,metadata) in order to know when the upload is complete, and only then will you be able to call getDownloadURL() successfully on its reference.

Related

firestore getting doc data issue

I trying to get the url of an image from firebase firestore, the upload goes successfully, but when I try to get the image and display it, it gives me this error:
TypeError: undefined is not an object (evaluating 'doc.push')
This is the method how I get datas:
useEffect(() => {
const result = db.collection("homeBackground").doc("bg_img").onSnapshot(snap => {
const doc = snap.doc;
setImages(doc.push(doc => doc.data()))
});
return result;
}, []);
This is how I display the image:
{
images.push((image) => {
return (
<img src={image.imageUrl} alt=""/>
)
})
}
onSnapshot() is a kind of asynchronous Observable method. You need to check are data exist then assign to any thing you want.
.onSnapshot(snap => {
if(snap.exists()) { // Some methods returns object with .exist value/function.
setImages(snap.data())
}
});
onSnapshot(snapshoot: QuerySnapshot<DocumentData> | DocumentSnapshot<DocumentData> => ...) This is what you get depending on on what you wariong on you can get generic object of querysnapshot or DocumentSnapshot.
This method will trigger when data will change in database. It returns unsubscription which you can invoke and stop observing database changes.
const unsubscription = db.collection("homeBackground").doc("bg_img").onSnapshot(snap => ...);
// later in time
unsubscription() // this will stop invoke method above.

Cannot show images after fetching from an API

I'm trying to chain two fetch request using axios. My code is :
const fetchCatsData = async () => {
const fetchBreeds = await axios.get("https://api.thecatapi.com/v1/breeds", {
headers: {
"x-api-key": "MY API KEY ",
},
})
await fetchBreeds.data.map(breed => {
axios.get(`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}&include_breeds=false`)
.then(res => breed.image_url = res.data[0].url)
})
dispatch({ type: FETCH_BREEDS, payload: fetchBreeds.data })
It succeeds and in react dev tools , i see a special key called 'image_url'inside my context , with the url of the image.I click on it's value and it open the requested image.
But when i'm trying to show the image in an image HTML tag , it shows nothing ...
Am i missing something ?
Thanks in advance
From the code here, i guess you are trying to wait until the image_url is set for all items in fetchBreeds.data. But it won't work in that way.
await fetchBreeds.data.map(breed => {
axios.get(`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}&include_breeds=false`)
.then(res => breed.image_url = res.data[0].url)
})
You use await on the map function. The map function is not async, so when you dispatch the action, the image_urls are not set yet. When you checked the store and found the image_url was there is because the fetchBreeds.data was mutated by axios call directly without using redux dispatch system. This didn't trigger the UI re-render so image didn't show. What happened is shown below:
dispatch({ type: FETCH_BREEDS, payload: fetchBreeds.data }) This happens first.
Component is notified so re-render. image_url is not set yet, so image is empty
.then(res => breed.image_url = res.data[0].url) This is called next. Because the function holds the reference of the fetchBreeds.data, so it changes the fetchBreeds.data object directly without using reducer.
UI is not notified so it doesn't know image_url is changed and won't re-render.
I suggest you change the function to:
await Promise.All(fetchBreeds.data.map(breed => {
return axios.get(`https://api.thecatapi.com/v1/images/search?breed_ids=${breed.id}&include_breeds=false`)
.then(res => breed.image_url = res.data[0].url)
}))

Value of state variable is lost - React

I want to build a CRUD in React with Laravel and Firebase. Everything is perfect when I'm working with text, but I got trouble when I try to upload an image to Firebase Storage. I can save it but I can't get its URL.
I wrote 2 "console.log". In the first one the URL is there, but the second one (when I try to get the URL from the state variable) doesn't return anything.
handleSubmit = event =>{
event.preventDefault();
const {imagen} = this.state;
if(imagen!=null){
const uploadTask = storage.ref(`imagenes/${imagen.name}`).put(imagen);
uploadTask.on('state_changed',
(snapshot) => {
const progress = Math.round((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
this.setState({progress});
},
(error) => {
console.log(error);
},
() => {
storage.ref('imagenes').child(imagen.name).getDownloadURL().then(url => {
this.setState({url});
console.log(this.state.url); //<<<<<<<<<<<<<SHOW URL (IT'S OK!)
})
});
}
var direccion = null;
const form = event.target;
let data = new FormData(form);
data.append('url', this.state.url);
console.log(this.state.url); //<<<<<<<DOESN'T SHOW URL !! (HERE'S THE TROUBLE)
If you want to check the entire file:
https://github.com/AndresVasquezPUCE/project/blob/master/pelicula
I'm not a professional, so please don't be rude :D
this.setState is asynchronous
If you want to get the updated state value, add a callback and access the new state there like
this.setState({ url: 'some url'}, () => {
conosle.log(this.state.url);
});
Data is loaded from Firebase asynchronously. By the time your console.log(this.state.url); //<<<<<<<DOESN'T SHOW URL !! (HERE'S THE TROUBLE) the data hasn't been loaded from Firebase yet, and the then hasn't been called yet.
Any code that needs the data from Firebase needs to either be inside the then() callback (such as console.log(this.state.url); //<<<<<<<<<<<<<SHOW URL (IT'S OK!)) or be called from there (such as this.setState({url})).

How to fix this function to handle different error type of error catching

I have developing mern stack web site. In that I have added below codes to handle logging.
onSubmit(e) {
e.preventDefault();
const obj = {
user_name: this.state.user_name,
password: this.state.password
};
axios.post('http://localhost:4000/login', obj)
.then(res=> localStorage.setItem('token',(res.data.token))
//localstorage.setItem('username','res.data.user.username)
)
}
When I click on login button this onSubmit() function called and will save token in local storage.
But, res.data have more details. (from backend it passes logged users information too)
So I want to add those to local storage. I tried that as commented in above function. It says error in res. Note : I user react for frontend.
Also I want to handle handle errors in any cases axios.post() didn't work as planned. In server side it send different messages for unmatched credentials and wrong passwords. How can I show those in my page. Thank you.
Since the only accepted data type in localStorage is string, you should stringify it first using JSON API.
const userDataStr = JSON.stringify(res.data);
localStorage.setItem('userData', userDataStr);
Now if you want to access the userData from localStorage you just need to convert it back to javascript object.
const userDataStr = localStorage.getItem('userData', userData);
const userData = JSON.parse(userDataStr);
You can have multiple catch in the returned promise of axios.post
axios.post()
.catch((error) => { })
.catch((error) => { })
But those catch will called with the same error so you need to handle it differently in each catch
Another suggestion:
If you want to easily handle the error, you can use higher order function like this
const handleError = (status, callback) => (error) => {
if (status === error) {
callback(error);
}
}
axios.post()
.catch(handleError(404, (error) => { /* only called when status === 404 */ }))
.catch(handleError(500, (error) => { /* only called when status === 500 */ }))

React Redux Firebase Upload File Object

Trying to pass the file object to redux action and perform the function inside of an redux action, not sure its the correct way? but basically i want back downloadURL from firebase upload complete so I can show image front end.
createLocation(event) {
event.preventDefault();
const fileObject = this.state.file;
const test = {
fileObject
}
this.props.uploadImage_func(test);
}
and action function:
export function uploadImage_func(fileObject) {
return dispatch => {
const fileName = 'myimage';
const storageRef = firebase.storage().ref('test/' + fileName);
const task = storageRef.put(fileObject);
task.on('state_changed',
function complete(snapshot) {
const downloadURL = task.snapshot.downloadURL;
},
).then(function () {
dispatch(attemptLogin({
...downloadURL
}));
});
}
}
error:
As you can see you have got an error Invalid argument in 'put' at index 0: Expected Blob or File. So first of all you need path exactly File or Blob. If you did right in you createLocation and got file object than you need not to wrap it in const test object more. That action causes unnecessary nesting, so just path fileObject as it is. And more. When you subscribe for firebase UploadTask on event you need to path callback functions and do it in a right order, so try to use next:
uploadTask.on('state_changed',
(snapshot) => {
// here you could log loading information in percents but uploading is not finished yes
console.log((snapshot.bytesTransferred / snapshot.totalBytes) * 100);
},
(error) => console.log(error),
() => {
// And after uploading is complete you could get your download url
console.log('Call save img', uploadTask.snapshot.downloadURL);
}
);
For more information read documentation for Firebase Storage (Upload files)

Resources