I am successfully batch uploading images to firebase, but for some reason, my data is not being uploaded afterwards.
I can confirm though, it does work perfectly ONLY on a fresh build for the first time? Super weird.
I am not sure what is going wrong as I am able to receive the urls fine after the fact.
Here is my function:
onUploadImages = () => {
let photo =
Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
Promise.all(
photo.map((image, index) => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${index}`);
return fs
.readFile(image, '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(results => {
//results is, here, [ urlFromFirst, urlFronSecond, ...]
const urls = {...this.state.urls};
results.forEach((r, i) => (urls[i] = r));
const postObj = {
...this.state.postObj,
urls,
};
//THIS WONT RUN (BUT I AM ABLE TO LOG PROPER postObj) here
return firebase
.firestore()
.collection('brandProducts')
.add(postObj);
})
.then(docRef => {
Actions.categories();
console.log('Document written with ID: ', docRef.id);
})
.catch(error => {
console.error(error);
});
};
Here is my postObj:
postObj: {
title: 'A Title',
price: 20,
description: 'Some text here',
webLink: 'a url as a string',
user: 'Username',
urls: {
'0': 'downloadUrl1',
'1': 'downloadUrl2',
'2': 'downloadUrl3',
}
},
I would appreciate any help i can get figuring out where this is going wrong. Cheers!
Related
I built a image detection mobile app (e.g. Plastic Bottle, Aluminum Can, Milk Jug, etc.) with React-Native by using google vision API.
It worked well before and got response successfully.
But after I add Firebase image uploading function for store image, it (google vision api) didn't work.
In my guess, Firebase image upload and google vision API seems conflict and not compatible with each other.
Or in my image upload function, there seems error, but I am still not sure what is issue. Following is my code.
const takePicture = async () => {
if (this.camera) {
const options = { quality: 0.5, base64: true };
const data = await this.camera.takePictureAsync(options);
setScannedURI(data.uri)
imageUploadToFirebase(data)
// callGoogleVisionApi(data.base64) //============> After comment image upload function(above line) and if I call vision api here, it works well.
setIsLoading(true)
}
};
const imageUploadToFirebase = (imageData) => {
const Blob = RNFetchBlob.polyfill.Blob; //firebase image upload
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const Fetch = RNFetchBlob.polyfill.Fetch
window.fetch = new Fetch({
auto: true,
binaryContentTypes: [
'image/',
'video/',
'audio/',
'foo/',
]
}).build()
let uploadBlob = null;
var path = Platform.OS === "ios" ? imageData.uri.replace("file://", "") : imageData.uri
var newItemKey = Firebase.database().ref().child('usersummary').push().key;
var _name = newItemKey + 'img.jpg';
setIsLoading(true)
fs.readFile(path, "base64")
.then(data => {
let mime = "image/jpg";
return Blob.build(data, { type: `${mime};BASE64` });
})
.then(blob => {
uploadBlob = blob;
Firebase.storage()
.ref("scannedItems/" + _name)
.put(blob)
.then(() => {
uploadBlob.close();
return Firebase.storage()
.ref("scannedItems/" + _name)
.getDownloadURL();
})
.then(async uploadedFile => {
setFirebaseImageURL(uploadedFile)
// callGoogleVisionApi(imageData.base64) //============> If I call here, it didn't work.
})
.catch(error => {
console.log({ error });
});
});
}
This is my callGoogleVisionApi function.
const callGoogleVIsionApi = async (base64) => {
let googleVisionRes = await fetch(config.googleCloud.api + config.googleCloud.apiKey, {
method: 'POST',
body: JSON.stringify({
"requests": [{
"image": { "content": base64 },
features: [
{ type: "LABEL_DETECTION", maxResults: 30 },
{ type: "WEB_DETECTION", maxResults: 30 }
],
}]
})
})
.catch(err => { console.log('Network error=>: ', err) })
await googleVisionRes.json()
.then(googleResp => {
if (googleResp) {
let responseArray = googleResp.responses[0].labelAnnotations
responseArray.map((item, index) => {
if (item.description != "" && item.description != undefined && item.description != null) {
newArr.push(item.description)
}
})
}
}).catch((error) => {console.log(error)})
}
Note: If I upload an image to firebase after getting the result from google vision api, the second call to vision api does not work.
I added my callGoogleVIsionApi function. (It is working well without Firebase image upload function.)
What will be the solution of this issue?
I found the reason, but I am still curious why.
Fetch blob and google vision seems conflict each other.
I changed Firebase image upload function, and it worked well.
Following is my modified Firebase image upload function.
const imageUploadToFirebase = () => {
var path = Platform.OS === 'ios' ? scannedURI.replace('file://', '') : scannedURI;
const response = await fetch(path)
const blob = await response.blob();
var newItemKey = Firebase.database()
.ref()
.child('usersummary')
.push().key;
var _name = newItemKey + 'img.jpg';
Firebase.storage()
.ref(_name)
.put(blob)
.then(() => {
return Firebase.storage()
.ref(_name)
.getDownloadURL();
})
.then(async uploadedFile => {
let image = selectImage(sendItem.name?.toLowerCase());
sendItem.image = image;
sendItem.scannedURI = uploadedFile;
AsyncStorage.getItem('#scanedItemList')
.then(res => {
if (res != null && res != undefined && res != '') {
let result = `${res}#${JSON.stringify(sendItem)}`;
AsyncStorage.setItem('#scanedItemList', result);
} else {
AsyncStorage.setItem(
'#scanedItemList',
JSON.stringify(sendItem),
);
}
})
.catch(err => console.log(err));
})
.catch(error => {
console.log({error});
});
}
I'm not sure if you are using #google-cloud/vision package (in the callGoogleVisionApi() function) but as far as I know that is meant to be used in server side and authenticate with a service account. As an alternative to this method, you can use Cloud Storage Triggers for Cloud functions which will trigger a function whenever a new file is uploaded and then use Cloud Vision API.
The Google Vision API can use a base64-encoded image, a publicly accessible HTTP URI, or a blob in google cloud storage.
In order to use an HTTP URI you should change the JSON payload from your callGoogleVisionAPI function from this:
{
"requests": [{
"image": { "content": base64 },
features: [
{ type: "LABEL_DETECTION", maxResults: 30 },
{ type: "WEB_DETECTION", maxResults: 30 }
],
}]
}
to this:
{
"requests": [{
"image": { "source": {"imageUri": 'https://PUBLIC_URI_FOR_THE_IMAGE' } },
features: [
{ type: "LABEL_DETECTION", maxResults: 30 },
{ type: "WEB_DETECTION", maxResults: 30 }
],
}]
}
You've got a better explanation here: Make a Vision API request.
I've been building an app with Firebase & React Native primarily using Firestore. I started to use Firestore and its been great, but for some reason when writing to Firestore, it is only working on the first attempt (when i remove the app, rebuild, and perform my write).
I tried to do the exact same thing except write to Firestore and everything works as expected.
I am also receiving no error!
Here is what I am doing:
export const addBrandProduct = (postObj) => {
return () => {
firebase
.firestore()
.collection('brandProducts')
.add(postObj)
.then((docRef) => {
console.log("Document written with ID: ", docRef.id);
Actions.categories();
})
.catch(error => {
console.error("Error adding document: ", error);
});
};
};
For more of a reference, here is my component code that calls addBrandProduct()
onUploadImages = () => {
let photo =
Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
photo.forEach((image, i) => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${i}`);
fs.readFile(image, '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 => {
//if this is the last uploaded image, post data to db
if (i === this.state.images.length - 1) {
const urls = {
...this.state.urls,
[i]: url,
};
const postObj = {
...this.state.postObj,
urls: urls,
};
this.props.addBrandProduct(postObj);
} else {
this.setState({
urls: {
...this.state.urls,
[i]: url,
},
});
}
})
.catch(error => {
console.log(error);
});
});
};
Basically, I am uploading a maximum of 3 images along with some data for it. In order to ensure I am uploading them all prior to adding the post data (writing to firestore) I am using a forEach and on the last upload, when it completes, I am calling the action to write the post data.
Edition
Hum addBrandProduct is a function that create another function.
So when you call this.props.addBrandProduct(postObj) nothing is sent to firestore, you just create a new function that should be called.
Maybe you can go out this stuff and call firebase directly, ensuring that everything works and then go back to the redux way if you still want to use it. I also make it parallelized instead of sequentials. Hope it help, hard to find the real problem when it can come from anywhere.
onUploadImages = () => {
let photo = Platform.OS === 'ios'
? this.state.images.map(img => img.uri.replace('file://', ''))
: this.state.images.map(img => img.uri);
Promise.all( photo.map( image => {
const sessionId = new Date().getTime();
const Blob = RNFetchBlob.polyfill.Blob;
//This is kind useless
//const fs = RNFetchBlob.fs;
//This is not used
//window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
//This is not adviced
//window.Blob = Blob;
let uploadBlob = null;
let mime = 'image/jpg';
const imageRef = firebase
.storage()
.ref('brandProducts/')
.child(`${this.props.userData.uid}`)
.child(`${sessionId}-${i}`);
return fs.readFile(image, 'base64')
.then(data => {
return RNFetchBlob.polyfill.Blob.build(data, {type: `${mime};BASE64`});
})
.then(blob => {
uploadBlob = blob;
return imageRef.put(blob, {contentType: mime});
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
});
))
.then( results => {
//results is, here, [ urlFromFirst, urlFronSecond, ...]
const urls = { ...this.state.urls};
results.forEach( (r, i) => urls[i] = r );
const postObj = {
...this.state.postObj,
urls
};
return firebase
.firestore()
.collection('brandProducts')
.add(postObj)
})
.then( docRef => {
console.log("Document written with ID: ", docRef.id);
})
.catch(error => {
console.error(error);
});
};
I have this post screen where I can pick an image from the camera roll and type a text and I want it to be saved in Firebase.
Here is my code in fire.js
addPost = async({text,localUri}) => {
const remoteUri = await this.uploadPhotoAsync(localUri)
return new Promise((res,rej) => {
this.firestore.collection("posts").add({
text,
uid: this.uid,
timestamp:this.timestamp,
image: remoteUri
})
.then(ref => {
res(ref)
})
.catch(error => {
rej(error)
})
})
}
uploadPhotoAsync = async uri => {
const path = `photos/${this.uid}/${Date.now()}.jpg`
return new Promise(async (res,rej) => {
const response = await fetch(uri)
const file = await response.blob()
let upload = firebase.storage().ref(path).put(file)
upload.on(firebase.storage.TaskEvent.STATE_CHANGED,snapshot => {},
err => {
rej(err)
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL()
res(url)
}
)
})
}
And here is my postscreen.js screen where I'm getting the error can't find variable atob,
please suggest me a solution.
handlePost = () => {
Fire.shared.addPost({text:this.state.text.trim(),
localUri:this.state.image })
.then(ref => {
this.setState({text:"",image:undefined})
this.props.navigation.goBack()
}).catch(error => {
alert(error)
})
}
pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing:true,
aspect:[4,3]
})
if(!result.cancelled) {
this.setState({image: result.uri})
}
}
By the way, I can see the image is saved in Firestore storage but I can't see the text and photo in the Firestore database
This is a bug in some versions of firebase.
A workaround is to import base64 in the app.js and define it in case it's not defined.
import {decode, encode} from 'base-64'
if (!global.btoa) { global.btoa = encode }
if (!global.atob) { global.atob = decode }
I want to save a bunch of Images to Firebase storage and it's saved very well "as known image by image " in Firebase Storage, so after I saved it I want to get all the Uri and put it into Real-time DB as an Array-object like this
but I'm tried here in this code and also save one image just like this!
So how to handle these to Get all the images in the Storage and then put them into an array in DB?
// Open Gallery
pickMultiple = () => {
ImagePicker.openPicker({
multiple: true
})
.then(images => {
this.setState({
images: images.map(i => {
return {
uri: i.path,
width: i.width,
height: i.height,
mime: i.mime
};
})
});
})
.catch(e => console.log(e));
};
_SaveImagesToFirebase = () => {
const uid = firebase.auth().currentUser.uid; // Provider
const { images } = this.state;
const provider = firebase.database().ref(`providers/${uid}`);
images.map(image => {
let file = image.uri;
const path = "Img_" + Math.floor(Math.random() * 1500 + ".jpg");
const ref = firebase
.storage()
.ref(`provider/${uid}/ProviderGalary/${path}`);
let imagesArray = [];
ref
.put(file)
.then(() => {
ref
.getDownloadURL()
.then(
images => {
console.log(images);
imagesArray.push({
uri: images
});
},
error => console.log(error)
)
.then(() => {
provider
.update({
Images: imagesArray
})
.then(() => console.log("done with imgs"));
});
console.log("#inside", imagesArray);
})
.then(() => {
setTimeout(() => {
this.props.navigation.navigate("Home");
}, 2000);
});
console.log("#OUT", imagesArray);
});
};
UH My bad, I just define imagesArray inside map() it should be outside! like this,
_SaveImagesToFirebase = () => {
const uid = firebase.auth().currentUser.uid; // Provider
const { images } = this.state;
const provider = firebase.database().ref(`providers/${uid}`);
=> let imagesArray = [];
images.map(image => {
let file = image.uri;
const path = "Img_" + Math.floor(Math.random() * 1500 + ".jpg");
const ref = firebase
.storage()
.ref(`provider/${uid}/ProviderGalary/${path}`);
ref
.put(file)
.then(() => {
ref
.getDownloadURL()
.then(
images => {
console.log(images);
imagesArray.push({
uri: images
});
},
error => console.log(error)
)
.then(() => {
provider
.update({
Images: imagesArray
})
.then(() => console.log("done with imgs"));
});
})
.then(() => {
setTimeout(() => {
this.props.navigation.navigate("Home");
}, 2000);
});
});
};
Could you show me source code about uploading multiple images? I have tried to upload multiple images to my firebase. So i'm using react-native-image-crop picker for select images, and then react native-fetch-blob for convert the images before upload to firebase. After select images, i'm looping the arrays then converting to fetch blob inside looping. But sometimes it works but sometimes the images url is empty. I hope i can find the answer here
Try this:
const uploadImages = (photos) => {
const uploadImagePromises = _.map(photos, (p, index) => uploadImage({ uri: p, imageName: "image_" + index }))
const urls = await Promise.all(uploadImagePromises)
console.log(urls);
}
const uploadImage = ({ uri, imageName }) => {
const Blob = RNFetchBlob.polyfill.Blob
const fs = RNFetchBlob.fs
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest
window.Blob = Blob
const mime = 'image/jpg'
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri
let uploadBlob = null
const imageRef = firebase.storage().ref('/images/').child(imageName)
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()
resolve(imageRef.getDownloadURL())
})
.catch(error => {
console.log("error", error);
reject()
})
})
}