How to save uploaded file url in firebase using react - reactjs

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)
});
}

Related

I am trying to upload multiple photos on firebase storage and then add to the firestore using url

after clicking add button it creates as many firestore documents as photos selected
so if I add 2 photos it will make 2 documents with these photos
imageUpload // this is a state
It looks like your post is mostly code; please add some more details.
stack overflow gives me this error so this is a dummy text
const addProduct = () => {
const collRef = collection(db, "products");
try {
const imageUrl = [];
if (imageUpload.length > 0) {
for (let i = 0; i < imageUpload.length; i++) {
const name = imageUpload[i].name + Date.now();
const storageRef = ref(storage, `img/${name}`);
const uploadTask = uploadBytesResumable(storageRef, imageUpload[i]);
uploadTask.on(
"state_changed",
(snapshot) => {
const progress =
Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) * 1000
) / 10;
console.log(`Upload #${i + 1} is ${progress}% done`);
},
(error) => {
console.log("error", error);
},
() => {
getDownloadURL(uploadTask.snapshot.ref)
.then(async (url) => {
imageUrl.push({ src: url });
await addDoc(collRef, {
title,
image: imageUrl,
});
})
.catch((error) => {
console.log(error);
});
}
);
}
}else{
console.log("Photo is not selected");
}
} catch (error) {
console.log("catch", error.message);
}
};

State Not Finished Setting before being used in useEffect

I am hosting a react app in aws amplify using the aws-serverless version of express as the REST API, which sits inside of a lambda function. A big problem that I am facing is that asynchronous jobs in aws-serverless express cause the lambda function to complete before the promises resolve. Leaving me with no data and no error handling. This caused me to bring a lot of the asynchronous work to the front end of the application.
The problem here is that I need to bring a large amount of data into state. Right now, I am using a delay workaround (shown below) but instead need a programatic way to make sure state is finished updating before being used in the second useEffect hook (dependent on odds & failedTries props) instead of using the delay functionality.
Any help would be greatly appreciated.
const App = ({ signOut }) => {
const [odds, setOdds] = useState([]);
const [updateTime,setUpdateTime] = useState(0);
const [failedTries,setFailedTries] = useState(0);
useEffect(() => {
const setNflOdds = async () => {
let response = await updateNflOdds();
let data = response;
setOdds(data);
};
setNflOdds();
setUpdateTime(1);
const interval = setInterval(() => {
setNflOdds();
setUpdateTime(updateTime => updateTime +1);
}, 100000);
return () => clearInterval(interval);
}, []);
useEffect(() => {
const s3Push = (() => {
if(!odds.length) {
setFailedTries(failedTries => failedTries + 1);
} else {
const delay = ms => new Promise(res => setTimeout(res, ms));
const nflOddsRefDelay = async() => {
*//This is the current workaround, wait ten seconds before pushing odds state up to the s3 bucket*
await delay(10000);
oddsS3Helper(odds);
};
nflOddsRefDelay()
}
});
s3Push();
}, [odds, failedTries]);
With the above indicated delay workaround this works for my use case (13k records inside of the array) but the data size is highly variable and I want to figure out a way that no matter the data size it brings the entire call up to the s3 bucket.
below is the content of the functions being called in the useEffect hook
const pushToS3 = async ( file, key ) => {
const creds = await Auth.currentCredentials()
const REGION = {region};
const s3Client = new S3Client({
credentials: Auth.essentialCredentials(creds),
region: REGION
});
const params = {
Bucket: {s3 bucket name}
Key: key,
Body: file,
};
s3Client.send(new PutObjectCommand(params));
console.log("file is sent");
};
const oddsS3Helper = (async (odds) => {
console.log("inside s3 helper: ",odds);
let csv = '';
let headers = Object.keys(odds[0]).join(',');
let values = odds.map(odd => Object.values(odd).join(',')).join('\n');
csv += headers + '\n' + values;
const buffedFile = csv;
const key = 'nflprops.csv'
const delay = ms => new Promise(res => setTimeout(res, ms));
const propRefDelay = async() => {
await delay(5000);
await postNflOdds();
};
pushToS3( buffedFile, key );
await propRefDelay();
});
async function getNflGames() {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {} // OPTIONAL
};
const data = await API.get(apiName, path, init);
return data;
};
async function getNflOdds(gameId) {
const apiName = {name of serverless API inside of lambda};
const path = {path name};
const init = {
headers: {}, // OPTIONAL
body: { gameId }
};
const data = await API.post(apiName, path, init);
return data;
};
async function updateNflOdds() {
const ojNflGames = await getNflGames();
const nflGameProps = [];
const nflOddsPush = ( async () => {
try {
await ojNflGames.data.map( async (game) => {
const ojNflOdds = await getNflOdds(game.id)
await ojNflOdds.data[0].odds.map((line) => {
nflGameProps.push(
{
gameId: game.id,
oddsId: line.id,
sports_book_name: line.sports_book_name,
name: line.name,
price: line.price,
checked_date: line.checked_date,
bet_points: line.bet_points,
is_main: line.is_main,
is_live: line.is_live,
market_name: line.market_name,
home_rotation_number: line.home_rotation_number,
away_rotation_number: line.away_rotation_number,
deep_link_url: line.deep_link_url,
player_id: line.player_id,
}
);
});
});
} catch (err) {
console.log("there was an error", err);
}
});
try {
await nflOddsPush();
} catch(err) {
console.log("odds push errored: ", err);
}
console.log("inside of updateNflOdds function: ",nflGameProps);
return nflGameProps;
};

waiting array of urls after uploading images to firebase storage and then stroing it again to firestore

const sendImageToFirebase = (e) => {
const promises = []
const urlsArray = []
// productimage is an array of image files
productImage.forEach((image, i) => {
var storageRef = firebase.storage().ref();
var uploadTask = storageRef.child(`${userDetailsFirebase.uid}/` + Math.random()).put(image);
promises.push(uploadTask.on('state_changed',
(snapshot) => {
},
(error) => {
console.log("error");
},
async () => {
const downloadurl = await uploadTask.snapshot.ref.getDownloadURL()
urlsArray.push(downloadurl)
}
))
})
Promise.all(promises).then(res => {
db.collection("products").doc(idGeneratedforProduct).set(
{
imageURL: urlsArray, //array of image urls
},
).then(e => {
}).catch(error => console.log("Error while sendig items to Firebase"))
})
}
I want to upload a multiple images to firebase storage. Here, sendImagToFirebase is a normal function in reactJs, and productimage is an array of image files. I want to wait for URL for each image files and then store all of them as an array to firestore. I would appreciate your input on how to do it?
You can create a function that receoves the ref and the file and returns the downloadURL. By calling it for each file with a Promise.all you get as result your array of downloadURLs:
const uploadFileAndGetDownloadURL = async (ref, file) => {
const snap = await ref.put(file);
const downloadURL = await snap.ref.getDownloadURL();
return downloadURL;
};
const sendImageToFirebase = async (e) => {
const promises = [];
productImage.forEach((image, i) => {
var storageRef = firebase.storage().ref();
var ref = storageRef.child(`${userDetailsFirebase.uid}/` + Math.random());
promises.push(uploadFileAndGetDownloadURL(ref, image));
});
//Your array with the urls
const urlsArray = await Promise.all(promises);
};

Firestore storing partial data to document

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);
});
}
}

React - within function setstate not updating state

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)
});
}
})
});
}

Resources