I have a Service inclusion form, which includes 2 file uploads, one for single file selector and one more for multiple file selector. On submit click calling a function to upload the files to firebase storage and saving the links.
I'm updating the 'fileURLs' and 'sp_License' state in MultifileuploadHandler method. But it is not updating the state. when I do console.log of newState I cannot see the updated states.
Received follwoing error on submit
'FirebaseError: Function DocumentReference.set() called with invalid data. Unsupported field value: a custom File object (found in field sp_License)'
any help appreciated.!
EDIT :-
I have updated my code.
On submit, before uploading the file,save data method is called. How can I wait till upload is done and then call SaveData Method?
handleSubmit = (e) => {
e.preventDefault();
let err = this.validate();
if (!err) {
this.setState({ loading: true,disChecked:false })
this.fileupload();
this.MultifileuploadHandler();
this.saveData();
}
}
saveData=()=>{
let uid = this.props.auth.uid;
let keysToRemove = ["loading", "checked", "disChecked", "open", "message"]
let newState = Object.entries({...this.state}).reduce((obj, [key, value]) => {
if(!keysToRemove.includes(key)){
obj[key] = value
}
return obj
}, {})
console.log(newState)
this.props.UpdateUserDetails(uid, newState,this.successMessage)
}
fileupload=()=>{
//single org file
const {fileURLs,sp_License}=this.state;
const Lfilename = this.state.sp_Name + '_' + new Date().getTime();
const uploadTask = storage.ref('License/' + Lfilename).put(sp_License);
uploadTask
.then(uploadTaskSnapshot => {
return uploadTaskSnapshot.ref.getDownloadURL();
})
.then(url => {
// orgFile.push({url});
this.setState({sp_License:url})
// orgFile=url
// console.log(orgFile)
},()=>{
console.log(sp_License)
});
}
MultifileuploadHandler = () => {
const {fileURLs,sp_License}=this.state;
let files=[];
var orgFile=[];
//multi pilots file
const storageRef = storage.ref();
this.state.sp_PilotsLicense.forEach((file) => {
storageRef
.child(`License/${file.name}`)
.put(file).then((snapshot) => {
return snapshot.ref.getDownloadURL();
}).then(url =>{
files.push({url});
console.log(url)
if(files.length===this.state.sp_PilotsLicense.length)
{
console.log('url')
this.setState({ fileURLs: files },()=>{
console.log(fileURLs)
});
}
})
});
}
Related
I'm trying to update user profile using react native expo I can only update all properties except image is giving me this Error :
[Unhandled promise rejection: FirebaseError: Function DocumentReference.update() called with invalid data. Unsupported field value: undefined (found in field userImg in document users ? please help
const [image, setImage] = useState(null);
const [uploading,setUploading] = useState(false)
const [ userData, setUserData] = useState(null);
useEffect(()=>{
const getUserData = async ()=>{
db.collection("users")
.doc(auth.currentUser?.uid)
.get()
.then(snap => {
setUserData(snap.data());
});
}
getUserData();
},[])
const updateProfile = async()=>{
let imgUrl = await uploadImage();
if(imgUrl == null && userData.userImg){
imgUrl = userData.userImg
}
db.collection("users")
.doc(auth.currentUser.uid)
.update({
name: userData.name,
userName: userData.userName,
email: userData.email,
phone: userData.phone,
address: userData.address,
userImg:userData.mgUrl
})
}
I can upload the image successfully but I can't fetch it from storage to fire store
const uploadImage = async ()=>{
if(image == null){
return null;
}
const blob = await new Promise((resolve, reject)=>{
const xhr = new XMLHttpRequest();
xhr.onload = function (){
resolve(xhr.response)
};
xhr.onerror = function (){
reject( new TypeError("Network request failed"))
};
xhr.responseType = "blob"
xhr.open("GET",image,true)
xhr.send(null)
});
const ref = firebase.storage().ref().child("images/" + new Date().toISOString())
const snapshot = ref.put(blob)
snapshot.on(
firebase.storage.TaskEvent.STATE_CHANGED,
()=>{
setUploading(true)
},
(error)=>{
setUploading(false)
console.log(error)
blob.close();
return;
},
()=>{
snapshot.snapshot.ref.getDownloadURL().then((url)=>{
setUploading(false);
// Alert.alert('Profile Updated', 'You profile Updated Successfully..!')
console.log('donwload:', url)
setUserData(url)
blob.close()
return null
})
}
)
}
}
so please help me out between I'm using React Native Expo and thank you so much
To start off, the error [Unhandled promise rejection: FirebaseError: Function DocumentReference.update() called with invalid data. Unsupported field value: undefined means that one or more fields has a null value but firebase doesn't allow you to store that.
In your uploadImage function you're able to upload your image fine when the image actually does exist but in cases that it doesn't you're returning null which is where the problem is. Ideally, you can return an empty string which is safe then in cases where you read the image you can just check if the string is empty or not.
Fix
Step 1
Change this
if(image == null){
return null;
}
To this
if(image == null){
return "";
}
Step 2
After you get the download URL your setUserData is replacing all the fields with the URL so consider changing it to
`
setUserData({...userData, imgUrl : url})
Step 3
Also realize that in your update() there is a typo for imgUrl change from
userImg:userData.mgUrl to userImg:userData.imgUrl to properly set the image using the step for line
Hope that fixes It :)
`
Check if below code helps you to upload a Video or Image using firebase.
const uploadImageToFirestore = async (res, type) => {
const uri = res.assets[0].uri;
const filename = uri.substring(uri.lastIndexOf('/') + 1);
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
const storage = getStorage(app);
const fileRef = ref(storage, filename);
const img = await fetch(uploadUri);
const bytes = await img.blob();
let metadata;
if (type == 'video') {
if (filename.includes("mov") || filename.includes("MOV")) {
metadata = {
contentType: 'video/quicktime'
}
} else {
metadata = {
contentType: 'video/mp4',
};
}
} else {
metadata = {
contentType: 'image/jpeg',
};
}
uploadBytes(fileRef, bytes, metadata).then(async (uploadTask) => {
console.log('task', uploadTask)
getDownloadURL(uploadTask.ref).then((url) => {
if (type == 'video') {
setVideoData(url);
} else {
setImageData(url);
}
});
}).catch((err) => {
alert('Error while uploading Image!')
console.log(err);
});
}
I have a issue with data saving to Firestore. I'm passing a list of url's to be saved to the document.
data sent in the following format
But it saving only the first data
firestore data
what may be issue? Please help.
update button click handler
handleUpdate=()=>{
const promises = [];
let files=[];
const {fileURLs,sp_License,sp_PilotsLicense}=this.state;
let err = this.validate();
if (!err) {
this.setState({ loading: true,disChecked:false })
// const Lfilename = this.state.sp_Name + '_' + new Date().getTime();
// const uploadTask = storage.ref('License/' + Lfilename).put(sp_License);
let orgFile='';
const promise1=this.uploadTaskPromise().then((res)=>{
console.log(res)
orgFile=res
});
promises.push(promise1)
console.log(orgFile)
//promises.push(this.uploadTaskPromiseMulti());
const promise2=this.uploadTaskPromiseMulti().then((res)=>{
console.log(res)
files=res
});
promises.push(promise2)
console.log(promise2)
Promise.all(promises).then(tasks => {
console.log('all uploads complete');
console.log(this.state)
if(this._mounted)
{
//this.saveData();
//console.log(orgFile.data)
this.setState({
fileURLs: files, -- here fileurls gets updated
sp_License:orgFile,
},()=>{
console.log(this.state)
this.saveData();
});
}
});
}
}
code of saveData method - I'm using react redux firebase method for saving data
saveData=()=>{
let uid = this.props.auth.uid;
let keysToRemove = ["loading", "checked", "disChecked", "open", "message",
"sp_NameError", "sp_PhoneError", "sp_emailError", 'usr_org_LicenseNumberError',
'sp_LicenseError','usr_org_StateConveredError','usr_org_DistConveredError',
'sp_NumberofEquipmentsError','sp_NumberofDronePilotsError','sp_PilotsLicense',
'sp_NumberofEquipmentsOwnedError','sp_ToolError','processingToolServiceError',
'processingToolDomainError','modalopen','EquipmentCount',
'PilotsCount','buttonName','isReady','redirect']
let newState = Object.entries({...this.state}).reduce((obj, [key, value]) => {
if(!keysToRemove.includes(key)){
obj[key] = value
}
return obj
}, {})
console.log(newState)
this.props.UpdateUserDetails(uid, newState,this.successMessage)
}
Action code
export const UpdateUserDetails= (id, droneSPDetails,func) => {
return (dispatch, getState, { getFirestore }) => {
const firestore = getFirestore()
firestore.collection('web_Users')
.doc(id)
.set({
...droneSPDetails,
sp_UpdatedOn: new Date(),
//sp_Status:"pending",
sp_ActiveFlag:"1",
},{ merge: true })
.then(() => {
func();
dispatch({ type: 'CREATE_DRONESP', droneSPDetails });
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
}
I have a Registration form which has many fields for submission.Form submission is working fine, saving all the details to firestore. I have input file upload, user can select multiple files, on submit, uploadin all the files to firebase storage ans saving the upload link in an array(FileURLs).
For Example if I have selected 3 files on submit when i do console.log of state I can see the FileURLs with 3 data but In firestore last value is not getting saved.
Code
handleSubmit = (e) => {
e.preventDefault();
const promises = [];
const {fileURLs,sp_License}=this.state;
let files=[];
let orgFile='';
let err = this.validate();
if (!err) {
this.setState({ loading: true,disChecked:false })
// this.fileupload();
// this.MultifileuploadHandler();
const Lfilename = this.state.sp_Name + '_' + new Date().getTime();
const uploadTask = storage.ref('License/' + Lfilename).put(sp_License);
promises.push(uploadTask);
uploadTask
.then(uploadTaskSnapshot => {
return uploadTaskSnapshot.ref.getDownloadURL();
})
.then(url => {
// orgFile.push({url});
// this.setState({sp_License:url})
orgFile=url
// console.log(orgFile)
},()=>{
console.log(sp_License)
});
//multi pilots file
const storageRef = storage.ref();
this.state.sp_PilotsLicense.forEach((file) => {
const uploadTask= storageRef
.child(`License/${file.name}`).put(file)
promises.push(uploadTask);
uploadTask.then((snapshot) => {
return snapshot.ref.getDownloadURL();
}).then(url =>{
files.push({url});
})
});
Promise.all(promises).then(tasks => {
console.log('all uploads complete');
console.log(this.state)
if(this._mounted)
{
console.log(orgFile)
this.setState({
fileURLs: files,
sp_License:orgFile,
},()=>{
console.log(this.state)
});
}
this.saveData();
});
}
}
saveData=()=>{
let uid = this.props.auth.uid;
let keysToRemove = ["loading", "checked", "disChecked", "open", "message"]
let newState = Object.entries({...this.state}).reduce((obj, [key, value]) => {
if(!keysToRemove.includes(key)){
obj[key] = value
}
return obj
}, {})
console.log(newState)
this.props.UpdateUserDetails(uid, newState,this.successMessage)
}
Action
export const UpdateUserDetails= (id, droneSPDetails,func) => {
console.log(droneSPDetails)
console.log(func)
return (dispatch, getState, { getFirestore }) => {
const firestore = getFirestore()
firestore.collection('users')
.doc(id)
.set({
...droneSPDetails,
sp_RegisteredOn: new Date(),
sp_Status:"pending",
sp_ActiveFlag:"1",
},{ merge: true })
.then(() => {
func();
dispatch({ type: 'CREATE_DRONESP', droneSPDetails });
})
.catch((error) => {
console.error("Error adding document: ", error);
});
}
}
This is the value passed to store on firestore (from console.log)
fileURLs: Array(3)
0: {url: "https://firebasestorage.googleapis.com/v0/b/dronew…=media&token=084bc59a-8087-43de-afdb-05d4192ec4d2"}
1: {url: "https://firebasestorage.googleapis.com/v0/b/dronew…=media&token=3d427621-6a8b-4ed7-b6ce-5c3940fe7ee6"}
2: {url: "https://firebasestorage.googleapis.com/v0/b/dronew…=media&token=e24a5bab-e584-4a95-bb70-4d6c9f7fed7c"}
length: 3
but I can see only these 2 values getting saved.
I am new to React Redux firebase. I want to upload the file and save the url in database. I'm able to save the file to storage, but cannot save the url.
Any answer will be appreciated!
on button click I have the following code.
handleSubmit = (e) => {
e.preventDefault();
const { sp_License } = this.state;
const Lfilename = this.state.sp_name + '_' + new Date().getTime();
const uploadTask = storage.ref('License/' + Lfilename).put(sp_License);
uploadTask.on('state_changed',
(snapshot) => {
//progress
},
(error) => {
//error
},
() => {
uploadTask.snapshot.ref.getDownloadURL().then(
url => this.setState({ License: url })
);
console.log(this.state);
this.props.createDroneSP(this.state)
});
}
Instead of using the on() method which sets a listener, you should use the then() method which "behaves like a Promise, and resolves with its snapshot data when the upload completes".
Therefore you should modify your code as follows:
handleSubmit = (e) => {
e.preventDefault();
const { sp_License } = this.state;
const Lfilename = this.state.sp_name + '_' + new Date().getTime();
const uploadTask = storage.ref('License/' + Lfilename).put(sp_License);
uploadTask
.then(uploadTaskSnapshot => {
return uploadTaskSnapshot.ref.getDownloadURL();
})
.then(url => {
this.setState({ License: url });
console.log(this.state);
this.props.createDroneSP(this.state)
});
}
I'm trying to run 3 methods after each other when a user clicks a button
Steps:
1: Push a file to IPFS, get the link back and assign it to a state variable
2: Add that link (from that var) to blockchain smart contract
3: Add an entry to the firebase database
The problem is that that IPFS link is null when I try to pass it to my smart contract, however after the method runs I can see the link print to console.
So I'm guessing it is not being set in time for the next method to see the variable.
IPFS Method:
pushToIPFS = async(e) => {
// e.preventDefault()
await ipfs.add(this.state.buffer, (err, ipfsHash) => {
console.log(err, ipfsHash)
//this.setState({IPFSlink : ipfsHash[0].hash})
console.log(ipfsHash[0].hash)
return ipfsHash[0].hash
})
}
Blockchain method:
addToBlockchain = async(e) => {
//create a new key for our student
var key = this.state.StudentNumber + this.state.account[0]
key = parseInt(hash(key), 10)
this.setState({idForBlockchain: key})
console.log(key)
//get todays date
let newDate = new Date()
newDate = newDate.getTime()
var _ipfsLink = this.state.IPFSlink
var _account = this.state.account[0]
console.log(_ipfsLink)
console.log(this.state.IPFSlink)
await storehash.methods.sendDocument(_ipfsLink, newDate,
}
Firebase method:
createStudent = async(e) => {
//get student details from state variables & current user uid
var _uid = this.state.uid
var _studentName = this.state.StudentName
var _studentNumber = this.state.StudentNumber
var _courseCode = this.state.CourseCode
var _courseName = this.state.CourseName
var _idForBlockchain = this.state.idForBlockchain
// database.ref.students.uid.studentNumber
const db = firebase.database()
db.ref().child("students").child(_uid).child(_studentNumber).set(
{ studentName: _studentName,
courseCode: _courseCode,
courseName: _courseName,
blockchainKey: _idForBlockchain
}
);
alert("Student added")
}
The method that triggers when the button is clicked:
AddMyStuff = async (e) => {
e.preventDefault()
await this.pushToIPFS()
await this.addToBlockchain()
await this.createStudent()
}
This is the error returned, so what I assume is the await and setState are causing issues and the variable I need is not being set.
Unhandled Rejection (Error): invalid string value (arg="_ipfsLocation", coderType="string", value=null, version=4.0.27)
Does anyone know how this can be solved?
you can convert your pushToIPFS to be a promise instead of a callback, and just resolve it when the callback is fired.
pushToIPFS = (e) => {
return new Promise((resolve, reject) => {
ipfs.add(this.state.buffer, (err, ipfsHash) => {
resolve(ipfsHash[0].hash);
})
});
}
And since its a promise you can use async/await.
AddMyStuff = async (e) => {
e.preventDefault()
const ipfsHash = await this.pushToIPFS();
//you have your ipfsHash defined, you can pass it to your other methods
}