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.
Related
I am working on my first Vue-Projeect with firebase.
I would like to create locations with particular images and other data.
I am working with a tutorial which is unfortunately a bit outdated. Currently I am struggeling with the connection of firebase storage with firebase database.
I am not able to push the downloadable imageUrl in the firebase storage and store it in the database.
Can you guys help me out here?
Thank you
createLocation( {commit, getters}, payload) {
const location = {
title: payload.title,
location: payload.location,
description: payload.description,
creationDate: payload.creationDate,
rating: payload.rating,
coordinates: payload.coordinates,
filters: payload.filters,
creatorId: getters.user.id
}
let imageUrl
let key
firebase.database().ref('locations').push(location)
.then((data) => {
key = data.key
return key
})
.then(key => {
const filename = payload.image.name
const ext = filename.slice(filename.lastIndexOf('.'))
console.log(payload.image)
return firebase.storage().ref('locations/' + key + ext).put(payload.image)
})
.then(fileData => {
imageUrl = fileData.metadata.getDownloadURL
return firebase.database().ref('meetups').child(key).update({imageUrl: imageUrl})
})
.then(() => {
commit('createLocation', {
...location,
imageUrl:imageUrl,
id: key
})
})
.catch((error) => {
console.log(error)
})
},
If you look at the reference documentation for metadata.downloadURL it says:
deprecated
Use Reference.getDownloadURL instead. This property will be removed in a future release.
Determining the download URL for a file now requires another roundtrip to the server, so you'll need another then block for that:
firebase.database().ref('locations').push(location)
...
.then(key => {
const filename = payload.image.name
const ext = filename.slice(filename.lastIndexOf('.'))
console.log(payload.image)
return firebase.storage().ref('locations/' + key + ext).put(payload.image)
})
.then(fileData => {
return fileData.ref.getDownloadURL();
})
.then(imageUrl => {
return firebase.database().ref('meetups').child(key).update({imageUrl: imageUrl})
})
...
I've googled about how to send and get the Multiple data on firebase.
but none of them worked for me. and some of them work strangely like my code.
My Code uploads multiple data. but it is uploaded in duplicate.
What I wanted to do is uploading different data separately.
Please let me know what I have to modify.
Here is My code.
const [file, setFile] = useState([]);
function handleChange(e) {
for (let i = 0; i < e.target.files.length; i++) {
const newFile = e.target.files[i];
setFile(newFile);
}
}
function uploadImageAsPromise() {
Promise.all(
[file].map((file) => {
return new Promise(() => {
const uploadTask = storage.ref(`files/${file.name}`).put(file);
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => {
console.log(snapshot);
storage
.ref("files")
.child(file.name)
.getDownloadURL()
.then((url) => {
//post image inside the db
const itemObj = {
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
caption: file.name,
fileUrl: url,
size: snapshot._delegate.bytesTransferred,
};
db.collection("myFiles").add(itemObj);
setUploading(false);
setOpen(false);
setFile(null);
});
})
}),
storage
.ref("files")
.child(file.name)
.getMetadata()
.then((meta) => {
console.log(meta.size);
})
}))
}
<input type="file" onChange={handleChange} multiple />
<button onClick={uploadImageAsPromise}>Upload</button>
I am trying to upload an Image to Firebase Storage and after successful upload I am trying to insert image downloadURL to Firestore collection at the same time.
How to do it? Here is the code in which Uploading Image is working fine but firestore is not working.
dbRef = firebase.firestore().collection('stack_overflow').doc()
constructor(props) {
super(props);
this.state = {
question_id: this.dbRef.id,
question_title: '',
question_image: null,
question_image_url: '',
};
}
dataOnChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value
this.setState(state)
}
handleImageOnChange = e => {
if (e.target.files[0]) {
const question_image = e.target.files[0]
this.setState(() => ({question_image}))
}
}
onSubmitData = (e) => {
e.preventDefault()
const {
question_id, question_title, question_image, question_image_url
} = this.state;
/*image upload and download*/
const uploadImage = storage.ref(`/stack_gallery/${cat_image.name}`).put(cat_image);
uploadImage.on('state_changed', (snapshot) => {
console.log("Image Upload Progress")
},
(error) => {
console.log(error);
},
/*() => {
storage.ref('quote_gallery').child(question_image.name).getDownloadURL().then(question_image_url => {
console.log(question_image_url);
this.setState({question_image_url});
})
}*/);
storage.ref('stack_gallery').child(question_image.name).getDownloadURL().then(question_image_url => {
console.log(question_image_url);
this.setState({question_image_url});
})
this.dbRef.set({
question_id, question_title, question_image_url
}).then((docRef) => {
this.add({
question_id: '',
question_title: '',
question_image_url: this.state.cat_image_url
})
}).catch((error) => {
console.error("Error Adding Document: ", error)
})
}
Please check my code where is problem and how to solve this problem .
As I see your code you saved question_image_url to this.state, and I think we dont have to save it to state as we use state to render JSX.
Once you got url in callback then why don't you run saving to firestore in callback?
And you used firestore().collection().doc().set, this works wrong. you should indicate the doc_id to set because set is updating function of current doc_id.
If you want to add new doc then you can use add function from collection that wrapps whole docs.
uploadImage.on('state_changed', (snapshot) => {
console.log("Image Upload Progress")
},
(error) => {
console.log(error);
},
function() {
uploadImage.snapshot.ref.getDownloadURL().then(function(downloadURL) {
firebase.firestore().collection('stack_overflow').add({
question_title: question_title,
question_image: question_image,
question_image_url: downloadURL
})
.then((res) => {
let id = res.id; //receive id of doc added
firebase.firestore().collection('stack_overflow').doc(id).set({question_id:id},{merge:true})
})
});
I'm trying to upload local images to Cloud Firestore and Firebase storage.
The PACKAGES I'm using are
react-native-image-picker to choose an image from local
rn-fetch-blob to transfer files as blob to Firebase storage
Here's the 2 functions I'm using:
export const uploadPost = async (postImageUri) => {
return new Promise(async(res, rej) => {
const fs = RNFetchBlob.fs;
fs.readFile(postImageUri,'hello','base64')
.then(async (data) => {
console.log('data from RNFetchBlob', data);
const storageRef = storage().ref(`posts/images`).child(`${data}`);
try {
storageRef.putFile(data,{contentType: 'image/jpeg'})
.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);
rej(error)
},
() => {
storageRef.getDownloadURL()
.then((downLoadUri) => {
console.log('File available at: ', downLoadUri);
// addPostInfo
res(downLoadUri)
})
}
)
}catch{err => console.log(err) }
})
} )
}
export const addPostInfo = async (post) => {
console.log('post.postImageUri in addPostInfo', post.postImageUri.slice(10))
const remoteUri = await uploadPost(post.postImageUri);
return new Promise((res, rej) => {
firestore()
.collection('posts')
.add({
id: post.id,
createdAt: post.date,
description: post.description,
image: remoteUri,
location: post.checkin_location
})
.then(ref => {
console.log('Finish adding in addPostInfo', ref)
res(ref)
})
.catch(err => rej(err))
})
}
And in the main screen, I directly send the image's path to the addPostInfo:
const _onSharingPost = () => {
const postCreated = {
id: 'alkdgjhfa;oiughaoghus',
postImageUri: postImageUri,
date: _getCurrentDate(),
description: postDescription,
checkin_location: checkInLocation
}
postActions.addPostInfo(postCreated);
}
The path is like this:
content://com.google.android.apps.photos.contentprovider/0/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F58/ORIGINAL/NONE/image%2Fjpeg/950379202
ERROR
...base64 string --> File name too long
It got down to the SUCCESS part of the upLoadPost function but then still got error
I HAVE TRIED:
Change the path --> cut off the content://
Use XMLHttpRequest to send the blob to the upLoadPost to send that to the storage
As far as I know, the problem must be the base64 string converted from the image path. I know we need to change that to blob or something, but I don't know how. I can't find where this can be specified in rn-fetch-blob docs
PLEASE HELP ME
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
});