Unable to use variables inside promise - javascript

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

Related

How to upload a blob into firebase Storage?

i'm using next js 13 with firebase v9. and i'm using a drop zone to upload images. the dropzone returns an array with blob as it's src.
[
{
id: 1
name: "image_processing20220628-4591-yzir35.png"
src: "blob:http://localhost:3000/6e2f33e5-a749-4e9a-b502-d20b8e3f38ca"
}
...
]
the above array is returned from the drop zone. and when i tried to upload to firebase storage it throws an error .
FirebaseError: Firebase Storage: Object 'livingImages/blob:http:/localhost:3000/ca0e3eaf-dbe9-4d77-8053-f4b6d1bd8600' does not exist. (storage/object-not-found)
so how can i upload blob of images to firebase storage?
const imgURL = [];
//this is the images stored inside Redux
const images = useSelector(selectImages);
const storage = getStorage();
images.map(async (file) => {
const storageRef = ref(storage, `livingImages/${file.src}`);
await getDownloadURL(storageRef).then((url) => {
imgURL.push(url);
});
});
const createDocument = () => {
const docRef = doc(db, "livingPosts", session?.user?.email);
const colRef = collection(docRef, "posts");
addDoc(colRef, {
name: "test upload",
images: imgURL,
});
};
the dropzone code
const dispatch = useDispatch();
const images = useSelector(selectImages);
const [files, setFiles] = useState(images == [] ? [] : images);
const {getRootProps, getInputProps} = useDropzone({
onDrop: (acceptedFiles) => {
acceptedFiles.map((file, index) => {
const reader = new FileReader();
reader.onload = async function (e) {
const options = {
maxSizeMB: 5,
maxWidthOrHeight: 1920,
useWebWorker: true,
};
const compressedFile = await imageCompression(file, options);
const tot = parseInt(acceptedFiles.length) + parseInt(files.length);
if (tot > 9) {
alert("select maximum of 9 images");
} else if (parseInt(acceptedFiles.length) > 9) {
alert("maximum images to be selected is 9");
} else if (parseInt(files.length) < 9) {
setFiles((prevState) => [
...prevState,
{
id: index,
src: URL.createObjectURL(compressedFile),
name: file.name,
},
]);
files.map((filename) => {
acceptedFiles.forEach((newFile) => {
if (newFile.name == filename.name) {
alert("a duplicate image is detected");
setFiles(
files,
files.filter((val) => val !== newFile)
);
}
});
});
} else {
alert("something went wrong");
}
};
reader.readAsDataURL(file);
return file;
});
},
})
and the output of the dropzone is
As mentioned in the comments, you'll need the actual File or Blob object to upload the file and not the object URL. You can set the blob in state as shown below:
setFiles((prevState) => [
...prevState,
{
id: index,
src: URL.createObjectURL(compressedFile),
blob: compressedFile, // <-- add blob
name: file.name,
},
]);
Then to upload the files and storing download URLs in Firestore document, try the following function:
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { addDoc } from "firebase/firestore";
const uploadFiles = async () => {
console.log(files);
const promises = files.map((file) => {
const storageRef = ref(storage, `images/${file.name}`);
return uploadBytes(storageRef, file.blob);
});
// upload all files
const res = await Promise.all(promises);
// get download URLs
const links = await Promise.all(res.map((r) => getDownloadURL(r.ref)));
console.log({ links })
// Add Firestore document
const colRef = collection(db, "livingPosts", session?.user?.email, "posts")
const docRef = await addDoc(colRef, {
name: "test",
images: links,
});
console.log("Document written with ID: ", docRef.id);
};
You can call this function on a submit button click or any event when you want to start the upload.

how to run useEffect only twice

Here is My useEffect is going in Infinite loop, becouse checkimage value is changing becouse the value is assigned in fetch(), so anyone know how to solve it. I want to get varient data with image but I can't get it in first time.
help me if you can
Thank You
useEffect(() => {
fetch({ pagination });
}, [checkimage]);
const fetch = async (params = {}) => {
if (type == 'product') {
dispatch(await ProductService.getProduct(productId))
.then((res) => {
let variantsdatas = getImageArns(res.data.variants);
getImages(variantsdatas);
let record = [];
record.push(res.data)
setVarientsData(record)
})
.catch((err) => {});
} else {
dispatch(await ProductService.getProducts())
.then((res) => {
console.info({ 'res.data': res.data });
setVarientsData(res.data.products);
setPagination({
...params.pagination,
total: res.total_count,
});
})
.catch((err) => {});
}
};
const getImageArns = (variantsdatas) => {
const variantImageArns = [];
variantsdatas.forEach((variant, index) => {
variant[index] = variant.variantId;
if (variant.variantImagesListResponseDto.images.length > 0) {
let variantImageObj = {
variantId: variant.variantId,
arnUrl: variant.variantImagesListResponseDto.images[0].docUrl,
};
variantImageArns.push(variantImageObj);
}
});
// console.info('id', variantImageArns);
return variantImageArns;
};
const getImages = async (variantsdatas) => {
const images = [];
dispatch(await ProductVariantService.getImage(variantsdatas))
.then((res) => {
console.info(res.data.fileResponseDtoList);
let presignedURLs = {};
res.data.fileResponseDtoList.map(
(i) => (
(presignedURLs = {
variantId: i.variantId,
arnUrl: i.presignedURL,
}),
console.info(presignedURLs),
images.push(presignedURLs)
)
);
setcheckimage(images);
})
.catch((err) => {
console.info('Get Error District...');
});
};
var img = 'img';
const setVarientsData = (products) => {
let varients_array = [];
if (products.length > 0) {
products.forEach((product) => {
if (product.variants.length > 0) {
let product_varients = product.variants;
product_varients.forEach((varient) => {
for (var f = 0; f < checkimage.length; f++) {
if(checkimage[f].variantId == varient.variantId){
img = checkimage[f].arnUrl;
f = checkimage.length
}
else{
img = 'img2';
}
}
varients_array.push({
image: img,
variantId: varient.variantId,
productVariantName: varient.variantName,
productName: product.productName,
brand: '-',
sellerSku: varient.productVariantCode,
status: product.status,
category: product.subCategoryInfo.categoryInfo.categoryName,
subCategoryName: product.subCategoryInfo.subCategoryName,
state: '-',
market: '-',
mrp: varient.price.amount + ' ' + varient.price.currency,
sellingPrice: '-',
manufacturer_product_variant_code:
varient.manufacturerProductVariantCode,
product_varient_code: varient.azProductVariantLongCode,
hsnCode: varient.hsnCode,
});
});
}
});
}
setVarients(varients_array);
console.info('varients_array ====>>', {varients_array})
};
I think that if I stop to run blow code getImage function then I can get my result
am I right?
But I tried It too but is also not happening properly.
a quick and dirty fix could be to work with a counter.
and only run the fetch in the useEffect, when counter is 0.
have you tried that?

trying to save to save data in asycstorage but unable to it so

here i have pasted my whole code , here i have api which has 400 + responses and it need to store in asyncstorage like it need to store in local database this api is limited,and people also suggesting me to write this in async await but i am not aware of async await could u pls edit the code and help me out and the important thing is I am using mobx state tree to access this code in another file. thanks in advance.
import {makeAutoObservable, action} from 'mobx';
import {Dimensions, Platform} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
const {width, height} = Dimensions.get('window');
import {encode} from 'base-64';
class ProductStore {
constructor() {
makeAutoObservable(this);
}
screenWidth = width;
screenHeight = height;
headerHeight = 0;
isiOS = Platform.OS === 'ios';
isAndroid = Platform.OS === 'android';
isProductLoading = 'pending';
productData = [];
filterdData = [];
search = '';
isFlatlistRender = false;
setFields(eName, data) {
this[eName] = data;
console.log(eName, data);
}
// searchFilter = text => {
// if (text) {
// // setIsfilterdData(ProductStore.productData);
// const fData = this.productData;
// const newData = fData.filter(item => {
// const itemData =
// item.product || item.id
// ? item.product.toUpperCase() || item.id
// : ''.toUpperCase();
// const textData = text.toUpperCase();
// return itemData.indexOf(textData) > -1;
// });
// this.filterdData = newData;
// this.search = text;
// } else {
// this.isFlatlistRender = true;
// this.search = text;
// }
// };
getproductData = async () => {
if (this.isProductLoading == 'loading') {
return true;
}
this.isProductLoading = 'loading';
this.productData = [];
let headers = new Headers();
headers.set(
'Authorization',
'Basic ' + encode('username:password'),
);
fetch('some_url', {
method: 'GET',
headers: headers,
})
.then(response => response.json())
.then(responseJson => {
console.log('.....', responseJson);
AsyncStorage.setItem('dataKey', JSON.stringify(responseJson));
// retrieving data whenever you need from local storage
AsyncStorage.getItem('dataKey')
.then(responseJson => {
if (responseJson) {
let ourData = JSON.parse(responseJson);
console.log('ourData >>>>> ', ourData);
this.productData = ourData;
this.isProductLoading = 'done';
}
})
.catch(err => console.log('error >>>>> ', err));
// this.productData = ourData;
// this.isProductLoading = 'done';
})
.catch(error => {
console.error(error);
this.isProductLoading = 'error';
});
};
}
export default new ProductStore();
if you want to use mobx i guess there is middleware to save the state in localStorage like Redux-Persist on redux
but if you want to use AsyncStorage only then u can do this.
the save one its already correct.
this one is get
//save this code on helper or something
export const getItem = async ()=>{
try{
const data = await AsyncStorage.getItem('dataKey');
if(!data) throw new Error("Data doesn't exist yet")
return [true,JSON.parse(data)];
//first array is boolean to check if it success or not
//2nd array is data or error message (if false)
} catch (e){
return [false,e.message];
}
}
and then you call it inside a didmount or useEffect and save it to state
import {getItem} from './FileLocation';
//for class component
async ComponentDidMount(){
const [success,data] = await getItem();
if(success){
this.setState({data})
} else{
Alert.alert('Error',data)
}
}
//functional
const [data,setData] = React.useState([]);
const FetchItem = async ()=>{
const [success,data] = await getItem();
if(success){
setData(data)
} else{
Alert.alert('Error',data)
}
}
React.useEffect(()=>{
FetchItem();
},[])

On Deploying the Firebase Cloud Function getting an error of "Each then should return a value or throw"

I am new to Cloud Functions so I having issues with below code, the error is in the last part where the console.log is mentioned, please assist what shall I been done to deploy the Function successfully, as I am following a tutorial there is no such error for the name.
'use-strict'
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.sendNotifications = functions.firestore.document("users/{user_id}/notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
return admin.firestore().collection("users").doc(user_id).collection("notifications").doc(notification_id).get().then(queryResult => {
const from_user_id = queryResult.data().from;
const message = queryResult.data().message;
const from_data = admin.firestore().collection("users").doc(from_user_id).get();
const to_data = admin.firestore().collection("user").doc(user_id).get();
return Promise.all([from_data, to_data]).then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
const payload = {
notification: {
title: "Notification from:" + from_name,
body: message,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload).then(result =>{
console.log("notifcation sent");
});
});
});
});
By chaining your Promises and returning null in the last then(), as follows, you should solve your problem:
exports.sendNotifications = functions.firestore.document("users/{user_id}/notifications/{notification_id}").onWrite(event => {
const user_id = event.params.user_id;
const notification_id = event.params.notification_id;
return admin.firestore().collection("users").doc(user_id).collection("notifications").doc(notification_id).get()
.then(queryResult => {
const from_user_id = queryResult.data().from;
const message = queryResult.data().message;
const from_data = admin.firestore().collection("users").doc(from_user_id).get();
const to_data = admin.firestore().collection("user").doc(user_id).get();
return Promise.all([from_data, to_data]);
})
.then(result => {
const from_name = result[0].data().name;
const to_name = result[1].data().name;
const token_id = result[1].data().token_id;
const payload = {
notification: {
title: "Notification from:" + from_name,
body: message,
icon: "default"
}
};
return admin.messaging().sendToDevice(token_id, payload)
})
.then(messagingResponse => {
console.log("notification sent");
return null; //Note the return null here, watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/
});
});
You may have a look at the corresponding MDN documentation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining
Also, note that, in your code, it seems that you are not using the to_name constant.

Convert image path to blob react native

Problem
I am trying to create an app with react native and firebase. One of the features I would like for this app is the ability to upload images. I am having some trouble uploading the images to firebase storage though. I am using expo's image picker to find the path of the image that the user wants to upload, but once I have the path I don't know how to convert that to something I can upload to firebase.
Can somebody help me convert the path of an image to something I can upload to firebase storage with react native?
What I've tried
I tried using:
_pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
MediaTypeOptions: 'Images',
quality: 0.4,
_uploadAsByteArray = async (pickerResultAsByteArray, progressCallback) => {
try {
var metadata = {
contentType: 'image/jpeg',
};
var storageRef = firebase.storage().ref();
var ref = storageRef.child('images/'+expoID+'/'+this.state.time)
let uploadTask = ref.put(pickerResultAsByteArray, metadata)
uploadTask.on('state_changed', function (snapshot) {
progressCallback && progressCallback(snapshot.bytesTransferred / snapshot.totalBytes)
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
}, function (error) {
console.log("in _uploadAsByteArray ", error)
}, function () {
var downloadURL = uploadTask.snapshot.downloadURL;
console.log("_uploadAsByteArray ", uploadTask.snapshot.downloadURL)
this.setState({imageUploaded:true})
});
} catch (ee) {
console.log("when trying to load _uploadAsByteArray ", ee)
}
}
convertToByteArray = (input) => {
var binary_string = this.atob(input);
var len = binary_string.length;
var bytes = new Uint8Array(len);
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes
}
atob = (input) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
let str = input.replace(/=+$/, '');
let output = '';
if (str.length % 4 == 1) {
throw new Error("'atob' failed: The string to be decoded is not correctly encoded.");
}
for (let bc = 0, bs = 0, buffer, i = 0;
buffer = str.charAt(i++);
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
buffer = chars.indexOf(buffer);
}
return output;
}
uploadImage(bsfdata){
this.setState({imageUploaded:false})
this._uploadAsByteArray(this.convertToByteArray(bsfdata), (progress) => {
this.setState({ progress:progress })
})
}
base64:true,
});
/* if (!result.cancelled) {
this.setState({ image: result.uri });
let formData = new FormData();
formData.append('photo', {
uri,
name: `photo.${fileType}`,
type: `image/${fileType}`,
});}*/
this.uploadImage(result.base64);
};
}
I've tried it with the commented code added, which doesn't upload anything, and I've tried it with how the code is now, which gives me the error Can currently only create a Blob from other Blobs, and the uploading progress never gets above 0%.
If you are using expo (>=26), then you can do it easily with the following lines of code.
uploadImage = async(imageUri) => {
const response = await fetch(imageUri);
const blob = await response.blob();
var ref = firebase.storage().ref().child("image.jpg");
return ref.put(blob);
}
Reference: https://youtu.be/KkZckepfm2Q
Refer this link - https://github.com/dailydrip/react-native-firebase-storage/blob/master/src/App.js#L43-L69
Following block of code is working fine.
uploadImage(uri, mime = 'application/octet-stream') {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
let uploadBlob = null
const imageRef = FirebaseClient.storage().ref('images').child('image_001')
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` })
})
.then((blob) => {
uploadBlob = blob
return imageRef.put(blob, { contentType: mime })
})
.then(() => {
uploadBlob.close()
return imageRef.getDownloadURL()
})
.then((url) => {
resolve(url)
})
.catch((error) => {
reject(error)
})
})
}
You need to install rn-fetch-blob module:
npm install --save rn-fetch-blob
Then, do the following:
import RNFetchBlob from 'rn-fetch-blob';
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
function uploadImage(path) {
const imageFile = RNFetchBlob.wrap(path);
// 'path/to/image' is where you wish to put your image in
// the database, if you would like to put it in the folder
// 'subfolder' inside 'mainFolder' and name it 'myImage', just
// replace it with 'mainFolder/subfolder/myImage'
const ref = firebase.storage().ref('path/to/image');
var uploadBlob = null;
Blob.build(imageFile, { type: 'image/jpg;' })
.then((imageBlob) => {
uploadBlob = imageBlob;
return ref.put(imageBlob, { contentType: 'image/jpg' });
})
.then(() => {
uploadBlob.close();
return ref.getDownloadURL();
})
.((url) => {
// do something with the url if you wish to
})
.catch(() => {
dispatch({
type: UPDATE_PROFILE_INFO_FAIL,
payload: 'Unable to upload profile picture, please try again'
});
});
}
Please do ask if there's any part of the code that you don't understand. To upload multiple images, simply wrap this code with a for loop. Or if you want to make sure that every image is uploaded without any error, use Promise
Not sure whom this might help, but if you're using MediaLibrary to load images from the gallery, then the uri comes in the format of uri = file:///storage/emulated/0/DCIM/Camera/filename.jpg
In this case, using fetch(uri) didn't help me get the blob.
But if you use fetch(uri.replace("file:///","file:/")) and then follow #sriteja Sugoor's answer, you'll be able to upload the file blob.
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
let uploadBlob;
await fs
.readFile(params?.file.path, 'base64')
.then((data) => {
return Blob.build(data, {type: `BASE64`});
})
.then((blob) => {
uploadBlob = blob;
console.log(uploadBlob, 'uploadBlob');
});

Categories

Resources