expo react native upload image to firebase storage - firebase

I am trying to upload an image from the phone library with expo-image-picker to Firebase storage and download the URL to save it in Firestore but for some reason, my app keeps crashing on (iPhone) without any error. I have tried every possible way to fix this issue(running my code line by line etc) but nothing has yet fixed this issue.
Has anyone encountered a similar issue and could help me with this particular problem? I have been stuck for a few days now. It would be a big help. Thank you in advance.
Here is my code:
Turning image to blob. at First, I used the fetch method but this seems to work better.
const urlToBlob = async (url) => {
return await new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.onerror = reject;
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
resolve(xhr.response);
}
};
xhr.open("GET", url);
xhr.responseType = "blob"; // convert type
xhr.send();
});
};
Uploading an image to storage. Sometimes it uploads but if you upload again it crashes.
const uploadImageAsync = async (imageUri) => {
let blob;
const imageRef = imageUri.substring(imageUri.lastIndexOf("/"));
try {
blob = await urlToBlob(imageUri);
const ref = await firebase.storage().ref().child(imageRef);
await ref.put(blob);
return await ref.getDownloadURL();
} catch (error) {
console.log(
"🚀 ~ file: eventServices.jsx ~ line 33 ~ createEvent ~ error",
error
);
} finally {
blob.close();
console.log("blob closed");
}
};
Here I get the image and pass it to my function which should return the URL to the image. URL then should get saved in Firestore.
export const createEvent = async (eventObj) => {
const imageUri = eventObj.image;
try {
const downloadUrl = uploadImageAsync(imageUri);
console.log(downloadUrl );
await firebase
.firestore()
.collection("events")
.add({ ...eventObj, image: downloadUrl });
console.log("Event added!");
} catch (error) {
console.log(
"🚀 ~ file: eventServices.jsx ~ line 62 ~ createEvent ~ error",
error
);
}
};

Related

#react-native-firebase/storage -How to upload Image and get the image url

I can upload Image but can't get url into firestore.
I have tried many other people's methods without success...
const uploadImage = async () => {
setUploading(true);
const response = await fetch(image.uri)
const blob = await response.blob();
const filename = image.uri.substring(image.uri.lastIndexOf('/') + 1);
**const imgurl = await getDownloadURL().catch((error) => { throw error });**
var ref = a.storage().ref('math/' + filename).child(filename).put(blob);
console.log("Document successfully written!")
try {
await ref;
} catch (e) {
console.log(e);
}
setUploading(false);
Alert.alert(
'sucess'
);
setImage(null);
db.collection("imageurl").doc().set({
url:imgurl,
})
}

firebase cloud function using getSignedUrl for thumbnail is expiring after about a week, no clear reason why?

I'm able to successfully generate a signedUrl for my thumbnails I am creating, but after about a week they no longer work...
I am not getting any errors or information as to why they are expiring, they just are.
I have been using firebase to develop my app and now all of a sudden I have to deal with all this google cloud storage permission and what not - really have no idea whats going on... It's got to be some sort of permission issue?
I have tried generating a new service account from the firebase console but no luck... I am tired of waiting weeks to see if they are going to expire again or not. I hope someone can guide me to a solution for this - it seems like its a problem for many people... We cant afford to go live and have gray thumbnails all over the app because they expire.
Here is how we are generating the signedUrl with firebase cloud functions:
export const generateThumbs = functions.storage
.object()
.onFinalize(async object => {
const fileBucket = object.bucket; // The Storage bucket that contains the file.
const filePath = object.name; // File path in the bucket.
const fileName = filePath.split('/').pop();
const userUid = filePath.split('/')[2];
const sizes = [150, 256];
const bucketDir = dirname(filePath);
if (!filePath.startsWith('categories/')) {
console.log('This is not in the categories directory.');
return false;
}
if (fileName.includes('thumb#') || !object.contentType.includes('image')) {
console.log('exiting function');
return false;
}
const bucket = gcs.bucket(fileBucket);
const tempFilePath = path.join(tmpdir(), fileName);
return bucket.file(filePath).download({
destination: tempFilePath
}).then(() => {
sizes.map(size => {
const newFileName = `thumb#${size}_${fileName}`
const newFileTemp = path.join(tmpdir(), newFileName);
const newFilePath = `thumbs/${newFileName}`
return sharp(tempFilePath)
.resize(size, size)
.toFile(newFileTemp, () => {
return bucket.upload(newFileTemp, {
destination: join(bucketDir, newFilePath),
metadata: {
contentType: 'image/jpeg'
}
}).then((data) => {
const file = data[0]
file.getSignedUrl({
action: 'read',
expires: '03-17-2100'
}, function(err, url) {
if (err) {
console.error(err);
return;
}
if (size === 150) {
return admin.database().ref('profileThumbs').child(userUid).child(fileName).set({ thumb: url });
} else if (size === 256) {
return admin.database().ref('categories').child(fileName).child('thumb').set(url)
.then(() => {
admin.database().ref('categories').child(fileName).child('tempThumb').remove();
})
}
})
})
})
})
}).catch(error =>{
console.log(error);
});
})
After setting the expiration date to 03-17-2100, we don't expect this type of behaviour, but like I said I feel like its something to do with gcs permissions - I tried to contact them but after about a week I am still waiting for their response.
I appreciate all the feedback!

Expo/Firebase: Image chosen from camera roll uploading as octet-stream instead of .jpg

I've been having trouble viewing the image files I've uploaded to firebase and just noticed the issue is with the file type in firebase.
Two files in my firebase storage console. One uploaded from my IOS simulator (octet-stream) and the other uploaded directly into the console from the browser which uploads properly and is viewable.
Here are my select and upload functions:
_selectPhoto = async () => {
const status = await getPermission(Permissions.CAMERA_ROLL);
if (status) {
let imageName = "pic"
const result = await ImagePicker.launchImageLibraryAsync(options);
if (!result.cancelled) {
Animated.timing(this.animatedWidth, {
toValue: 600,
duration: 15000
}).start()
this.uploadImage(result.uri, imageName)
.then(() => {
this.props.navigation.navigate('Profile')
})
.catch((error) => {
Alert.alert('Must Sign In');
this.props.navigation.navigate('Login')
console.log(error);
})
}
}
};
uploadImage = async (uri, imageName) => {
const user = firebase.auth().currentUser;
const response = await fetch(uri);
const blob = await response.blob();
let storageRef = firebase.storage().ref().child(''images/'+user.displayName+'/'+imageName+'.jpg'');
const snapshot = await storageRef.put(blob);
blob.close();
snapshot.ref.getDownloadURL().then(function(downloadURL) {
console.log("File available at", downloadURL);
user.updateProfile({
photoURL: downloadURL.toString(),
}).then(function() {
console.log('saved photo')
}).catch(function(error) {
console.log('failed photo')
});
});
}
When I get the link in my console, it also has the media&token:
... .appspot.com/o/profile-pic.jpg?alt=media&token=56eb9c36-b5cd-4dbb-bec1-3ea5c3a74bdd
If I CMD+Click in VS Code I receive an error:
{
error: {
code: 400,
message: "Invalid HTTP method/URL pair."
}
}
So naturally, when I put that link in the browser it downloads a file with that name but says:
The file “pic.jpg” could not be opened.
It may be damaged or use a
file format that Preview doesn’t recognize.
Maybe it could be something with mediaTypes, but I'm not exactly sure how to use it.
mediaTypes : String -- Choose what type of media to pick. Usage:
ImagePicker.MediaTypeOptions., where is one of: Images,
Videos, All.
Thanks!
I've been fighting with this same issue for the past few days. I was finally able get images to upload and render as expected by following the Firebase Upload example in the Expo repo. I don't fully understand why it works, but it seems like Firebase doesn't like the blob that's generated by
const blob = await response.blob();
Try replacing the above with:
const blob = await new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
resolve(xhr.response);
};
xhr.onerror = function(e) {
console.log(e);
reject(new TypeError('Network request failed'));
};
xhr.responseType = 'blob';
xhr.open('GET', uri, true);
xhr.send(null);
});

A base64 image is not stored properly in cloud storage for firebase (aka firebase storage)

I am uploading a base64 image to cloud storage for firebase (aka firebase storage), using the firebase web interface.
The code that I am using for uploading (in React Native) is the following:
const ref = await firebase.storage().ref(user).child(curTime.toString());
await ref.putString(base64String, 'base64', { contentType: 'image/jpeg' })
However, when I try to view the image it cannot be displayed.
The uploaded string is available in my dropbox, here (download to view it).
The file in firebase cloud storage is available here.
I have downloaded this file into my dropbox, you can access it here (download to view it).
Here is the relevant code:
const uploadImageAsync = async(userId, curTime, photoUri) => {
const user = normalizeUserId(userId); // prepare for firebase write
let firebaseImageUrl = null;
let base64String;
try {
base64String = await FileSystem.readAsStringAsync(photoUri,
{ encoding: FileSystem.EncodingTypes.Base64 });
} catch (error) {
console.log(error);
return null;
}
const ref = await firebase.storage().ref(user).child(curTime.toString());
await ref.putString(base64String, 'base64', { contentType: 'image/jpeg' })
.then(async(snapshot) => {
await snapshot.ref.getDownloadURL().then(uploadURL => {
firebaseImageUrl = uploadURL;
}, (error) => { // failed to get uploadedURL location
console.log(error);
return null;
});
}, (error) => { // failed to upload image to firebase storage);
console.log(error);
return null;
});
return firebaseImageUrl;
};

Catch in Angularfire2 v5 error

I am converting the use of Firebase Storage to use the Angularfire2 library (currently v5.0.0-rc.5-next) which means I am now using observables rather than promises.
How can I catch error such as storage/object-not-found and react accordingly?
This is currently my code but I cannot add a catch to it as some examples I found.
const avatarRef = this.afStorage.ref('${userId}/avatar/${this.avatarThumbnail}${user.avatar}');
avatarRef.getDownloadURL()
.take(1)
.subscribe((avatarUrl) => {
resolve(avatarUrl);
});
At its most basic, observers take an error callback to receive any unhandled errors in an observable stream. getDownloadURL() returns Observable that is why you need to subscribe. If you get an error (file not found or other) you will invoke code from error callback only.
avatarRef.getDownloadURL()
.take(1)
.subscribe((avatarUrl) => {
// Do something with avatarUrl here
console.log(avatarUrl);
}, (error) => {
// Handle error here
// Show popup with errors or just console.error
console.error(error);
});
Also I suggest you to read articles about error handling using RxJS and difference between Observable and Promise: link1, link2
The following solution work for me
startUpload(file) {
// The storage path
const path = `image${new Date().getTime()}.jpg`;
// Reference to storage bucket
const ref = this.storage.ref(path);
let image = 'data:image/jpeg;base64,' + file;
// The main task
return new Promise((resolve, reject) => {
const upload = ref.putString(image, 'data_url');
const sub = upload.snapshotChanges().pipe(
finalize(async () => {
try {
const photoURL = await ref.getDownloadURL().toPromise();
this.message.senderUid = this.currentUser.uid;
this.message.receiverUid = this.selectedUser.uid;
this.message.text = this.inputText && this.inputText !== '' ? this.inputText : 'File';
this.message.senderName = this.currentUser.name;
this.message.chatId = this.chatId;
this.message.file = photoURL;
this.firebaseService.insertMessage(this.message)
.then(() => {
this.inputText = '';
this.message.file = null;
this.scrollToBottomOnInit();
});
resolve({photoURL})
} catch (err) {
this.inputText = '';
this.message.file = null;
reject(err)
}
sub.unsubscribe()
})
).subscribe((data) => {
console.log('storage: ', data)
})
})
}
Source: https://github.com/angular/angularfire/issues/1736#issuecomment-515798352

Resources